From 82c79fd95d9bd7c9cb4326967d416c6067fb4944 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Thu, 17 Oct 2024 00:23:07 -0700 Subject: [PATCH 01/22] Adding the new sockets header --- include/wil/sockets.h | 866 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 866 insertions(+) create mode 100644 include/wil/sockets.h diff --git a/include/wil/sockets.h b/include/wil/sockets.h new file mode 100644 index 00000000..eb725f5b --- /dev/null +++ b/include/wil/sockets.h @@ -0,0 +1,866 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +//! @file +//! Helpers for using BSD sockets and Windows Winsock APIs and structures. +//! Does not require the use of the STL or C++ exceptions (see _nothrow functions) +#ifndef __WIL_SOCKETS_INCLUDED +#define __WIL_SOCKETS_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +// including winsock2.h before windows.h, to prevent inclusion of the older Winsock 1.1. header winsock.h in windows.h +// alternatively, one can define WIN32_LEAN_AND_MEAN to avoid windows.h including any sockets headers +// define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* APIs + +#if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) +#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid it included with Windows.h +#endif + +#include +#include +#include +#include +#include + +// wil headers +#include "resource.h" + +namespace wil +{ +//! Functions and classes that support network sockets operations and structures +namespace sockets +{ + + // encapsulates working with the sockaddr datatype + // + // sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) + // 'derived' structures are cast back to sockaddr* (so the initial struct members must be aligned) + // + // this data type was built to be 'extensible' so new network types could create their own address structures + // - appending fields to the initial fields of the sockaddr + // + // note that the address and port fields of TCPIP sockaddr* types were designed to be encoded in network-byte order + // - hence the common use of "host-to-network" and "network-to-host" APIs, e.g. htons(), htonl(), ntohs(), ntohl() + // + // Socket APIs that accept a socket address will accept 2 fields: + // - the sockaddr* (the address of the derived sockaddr type, cast down to a sockaddr*) + // - the length of the 'derived' socket address structure referenced by the sockaddr* + // + // Commonly used sockaddr* types that are using with TCPIP networking: + // + // sockaddr_storage / SOCKADDR_STORAGE + // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address + // + // sockaddr_in / SOCKADDR_IN + // - a sockaddr* derived type designed to contain an IPv4 address and port number + // sockaddr_in6 / SOCKADDR_IN6 + // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info + // SOCKADDR_INET + // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address + // + // in_addr (IN_ADDR) + // - the raw address portion of a sockaddr_in + // in6_addr (IN6_ADDR) + // - the raw address portion of a sockaddr_in6 + // + // SOCKET_ADDRESS + // - not a derived sockaddr* type + // - a structure containing both a sockaddr* and its length fields + // + // SOCKADDR_DL... data-link + + class addr_info; + addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; + class addr_info + { + public: + struct iterator + { + // TODO + }; + + iterator begin(); + iterator end(); + + [[nodiscard]] int last_error() const WI_NOEXCEPT + { + return m_lastError; + } + + addr_info(const addr_info&) = delete; + addr_info& operator=(const addr_info&) = delete; + + addr_info(addr_info&& rhs) WI_NOEXCEPT + { + m_addrResult = rhs.m_addrResult; + rhs.m_addrResult = nullptr; + } + + addr_info& operator=(addr_info&& rhs) WI_NOEXCEPT + { + if (m_addrResult) + { + ::FreeAddrInfoW(m_addrResult); + } + m_addrResult = rhs.m_addrResult; + rhs.m_addrResult = nullptr; + + return *this; + } + + ~addr_info() WI_NOEXCEPT + { + if (m_addrResult) + { + ::FreeAddrInfoW(m_addrResult); + } + } + + private: + friend addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; + addr_info(_In_ ADDRINFOW* addrResult, int error) : m_addrResult{addrResult}, m_lastError{error} + { + } + + ADDRINFOW* m_addrResult{}; + int m_lastError{}; + }; + + inline addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT + { + ADDRINFOW* addrResult{}; + int lastError = 0; + if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) + { + lastError = ::WSAGetLastError(); + } + + return {addrResult, lastError}; + } + + // INET6_ADDRSTRLEN is guaranteed to be larger than INET_ADDRSTRLEN for IPv4 addresses + typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; + typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; + + class socket_address final + { + public: + explicit socket_address(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; + template + explicit socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT; + explicit socket_address(const SOCKADDR_IN*) WI_NOEXCEPT; + explicit socket_address(const SOCKADDR_IN6*) WI_NOEXCEPT; + explicit socket_address(const SOCKADDR_INET*) WI_NOEXCEPT; + explicit socket_address(const SOCKET_ADDRESS*) WI_NOEXCEPT; + explicit socket_address(const IN_ADDR*, uint16_t port = 0) WI_NOEXCEPT; + explicit socket_address(const IN6_ADDR*, uint16_t port = 0) WI_NOEXCEPT; + + ~socket_address() = default; + socket_address(const socket_address&) WI_NOEXCEPT = default; + socket_address& operator=(const socket_address&) WI_NOEXCEPT = default; + socket_address(socket_address&&) WI_NOEXCEPT = default; + socket_address& operator=(socket_address&&) WI_NOEXCEPT = default; + + bool operator==(const socket_address&) const WI_NOEXCEPT; + bool operator!=(const socket_address&) const WI_NOEXCEPT; + bool operator<(const socket_address&) const WI_NOEXCEPT; + bool operator>(const socket_address&) const WI_NOEXCEPT; + + void swap(socket_address&) WI_NOEXCEPT; + void reset(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; + + // set_sockaddr overwrites the entire sockaddr in the object + + template + void set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT; + void set_sockaddr(const SOCKADDR_IN*) WI_NOEXCEPT; + void set_sockaddr(const SOCKADDR_IN6*) WI_NOEXCEPT; + void set_sockaddr(const SOCKADDR_INET*) WI_NOEXCEPT; + void set_sockaddr(const SOCKET_ADDRESS*) WI_NOEXCEPT; + + [[nodiscard]] bool is_address_any() const WI_NOEXCEPT; + [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; + [[nodiscard]] bool is_address_loopback() const WI_NOEXCEPT; + + // returns NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, + [[nodiscard]] NL_ADDRESS_TYPE get_address_type() const WI_NOEXCEPT; + + void set_port(USHORT) WI_NOEXCEPT; + void set_scope_id(ULONG) WI_NOEXCEPT; + void set_flow_info(ULONG) WI_NOEXCEPT; + + // set_address* preserves the existing address family and port in the object + + void set_address_any() WI_NOEXCEPT; + void set_address_loopback() WI_NOEXCEPT; + void set_address(const IN_ADDR*) WI_NOEXCEPT; + void set_address(const IN6_ADDR*) WI_NOEXCEPT; + [[nodiscard]] HRESULT set_address_nothrow(SOCKET) WI_NOEXCEPT; + [[nodiscard]] HRESULT set_address_nothrow(_In_ PCWSTR) WI_NOEXCEPT; +#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS // ANSI functions are deprecated + [[nodiscard]] HRESULT set_address_nothrow(_In_ PCSTR) WI_NOEXCEPT; +#endif + + // write_address prints the IP address portion, not the scope id or port +#if defined(_STRING_) || defined(WIL_DOXYGEN) + [[nodiscard]] std::wstring write_address() const; +#endif + HRESULT write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; + HRESULT write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; + + // write_complete_address prints the IP address, scope id, and port +#if defined(_STRING_) || defined(WIL_DOXYGEN) + [[nodiscard]] std::wstring write_complete_address() const; +#endif + HRESULT write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; +#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS // ANSI functions are deprecated + HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; +#endif + + // Accessors + [[nodiscard]] ADDRESS_FAMILY family() const WI_NOEXCEPT; + [[nodiscard]] USHORT port() const WI_NOEXCEPT; + [[nodiscard]] ULONG flow_info() const WI_NOEXCEPT; + [[nodiscard]] ULONG scope_id() const WI_NOEXCEPT; + + [[nodiscard]] SOCKADDR* sockaddr() WI_NOEXCEPT; + [[nodiscard]] SOCKADDR_IN* sockaddr_in() WI_NOEXCEPT; + [[nodiscard]] SOCKADDR_IN6* sockaddr_in6() WI_NOEXCEPT; + [[nodiscard]] SOCKADDR_INET* sockaddr_inet() WI_NOEXCEPT; + [[nodiscard]] IN_ADDR* in_addr() WI_NOEXCEPT; + [[nodiscard]] IN6_ADDR* in6_addr() WI_NOEXCEPT; + + [[nodiscard]] const SOCKADDR* sockaddr() const WI_NOEXCEPT; + [[nodiscard]] const SOCKADDR_IN* sockaddr_in() const WI_NOEXCEPT; + [[nodiscard]] const SOCKADDR_IN6* sockaddr_in6() const WI_NOEXCEPT; + [[nodiscard]] const SOCKADDR_INET* sockaddr_inet() const WI_NOEXCEPT; + [[nodiscard]] const IN_ADDR* in_addr() const WI_NOEXCEPT; + [[nodiscard]] const IN6_ADDR* in6_addr() const WI_NOEXCEPT; + + [[nodiscard]] static int length() WI_NOEXCEPT + { + return c_sockaddr_size; + } + + private: + static constexpr int c_sockaddr_size = sizeof(SOCKADDR_INET); + SOCKADDR_INET m_sockaddr{}; + }; + + // for dual-mode sockets, when needing to explicitly connect to the target IPv4 address, + // - one must map the IPv4 address to its mapped IPv6 address + inline socket_address map_dual_mode_4to6(const socket_address& inV4) WI_NOEXCEPT + { + constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; + + socket_address outV6{&v4MappedPrefix, inV4.port()}; + + auto* const pIn6Addr = outV6.in6_addr(); + const auto* const pIn4Addr = inV4.in_addr(); + pIn6Addr->u.Byte[12] = pIn4Addr->S_un.S_un_b.s_b1; + pIn6Addr->u.Byte[13] = pIn4Addr->S_un.S_un_b.s_b2; + pIn6Addr->u.Byte[14] = pIn4Addr->S_un.S_un_b.s_b3; + pIn6Addr->u.Byte[15] = pIn4Addr->S_un.S_un_b.s_b4; + + return outV6; + } + + // non-member swap + inline void swap(socket_address& lhs, socket_address& rhs) WI_NOEXCEPT + { + lhs.swap(rhs); + } + + inline socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT + { + m_sockaddr.si_family = family; + } + + template + socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + { + set_sockaddr(addr, inLength); + } + + inline socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT + { + set_sockaddr(addr); + } + + inline socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT + { + set_sockaddr(addr); + } + + inline socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT + { + set_sockaddr(addr); + } + + inline socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + { + set_sockaddr(addr->lpSockaddr, addr->iSockaddrLength); + } + + inline socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT + { + m_sockaddr.si_family = AF_INET; + set_address(addr); + set_port(port); + } + + inline socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT + { + m_sockaddr.si_family = AF_INET6; + set_address(addr); + set_port(port); + } + + inline bool socket_address::operator==(const socket_address& rhs) const WI_NOEXCEPT + { + const auto& lhs = *this; + + // Follows the same documented comparison logic as GetTcpTable2 and GetTcp6Table2 + if (lhs.family() != rhs.family()) + { + return false; + } + + if (lhs.family() == AF_INET) + { + // don't compare the padding at the end of the SOCKADDR_IN + return ::memcmp(&lhs.m_sockaddr.Ipv4, &rhs.m_sockaddr.Ipv4, sizeof(SOCKADDR_IN) - sizeof(SOCKADDR_IN::sin_zero)) == 0; + } + return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) == 0; + } + + inline bool socket_address::operator!=(const socket_address& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + inline bool socket_address::operator<(const socket_address& rhs) const WI_NOEXCEPT + { + const auto& lhs = *this; + // Follows the same documented comparison logic as GetTcpTable2 and GetTcp6Table2 + if (lhs.family() != rhs.family()) + { + return lhs.family() < rhs.family(); + } + + if (lhs.family() == AF_INET) + { + // don't compare the padding at the end of the SOCKADDR_IN + return ::memcmp(&lhs.m_sockaddr.Ipv4, &rhs.m_sockaddr.Ipv4, sizeof(SOCKADDR_IN) - sizeof(SOCKADDR_IN::sin_zero)) < 0; + } + return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) < 0; + } + + inline bool socket_address::operator>(const socket_address& rhs) const WI_NOEXCEPT + { + return !(*this < rhs); + } + + inline void socket_address::swap(socket_address& addr) WI_NOEXCEPT + { + SOCKADDR_INET tempAddr{}; + ::CopyMemory(&tempAddr, &addr.m_sockaddr, c_sockaddr_size); + ::CopyMemory(&addr.m_sockaddr, &m_sockaddr, c_sockaddr_size); + ::CopyMemory(&m_sockaddr, &tempAddr, c_sockaddr_size); + } + + inline void socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT + { + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + m_sockaddr.si_family = family; + } + + template + void socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + { + const size_t length = static_cast(inLength) < c_sockaddr_size ? inLength : c_sockaddr_size; + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + ::CopyMemory(&m_sockaddr, addr, length); + } + + inline void socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT + { + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + ::CopyMemory(&m_sockaddr.Ipv4, addr, sizeof(SOCKADDR_IN)); + } + + inline void socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT + { + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + ::CopyMemory(&m_sockaddr.Ipv6, addr, sizeof(SOCKADDR_IN6)); + } + + inline void socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT + { + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + ::CopyMemory(&m_sockaddr, addr, sizeof(SOCKADDR_INET)); + } + + inline void socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + { + FAIL_FAST_IF_MSG( + addr->iSockaddrLength > c_sockaddr_size, + "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", + addr->iSockaddrLength); + + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + if (addr->lpSockaddr) + { + ::CopyMemory(&m_sockaddr, addr->lpSockaddr, addr->iSockaddrLength); + } + } + + inline bool socket_address::is_address_any() const WI_NOEXCEPT + { + if (scope_id() != 0) + { + return false; + } + + switch (family()) + { + case AF_UNSPEC: + return false; + + case AF_INET: + return ::IN4_IS_ADDR_UNSPECIFIED(in_addr()); + + case AF_INET6: + return ::IN6_IS_ADDR_UNSPECIFIED(in6_addr()); + } + + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } + + inline bool socket_address::is_address_linklocal() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return false; + + case AF_INET: + return ::IN4_IS_ADDR_LINKLOCAL(in_addr()); + + case AF_INET6: + return ::IN6_IS_ADDR_LINKLOCAL(in6_addr()); + } + + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } + + inline bool socket_address::is_address_loopback() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return false; + + case AF_INET: + return ::IN4_IS_ADDR_LOOPBACK(in_addr()); + + case AF_INET6: + return ::IN6_IS_ADDR_LOOPBACK(in6_addr()); + } + + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } + + inline NL_ADDRESS_TYPE socket_address::get_address_type() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return NlatUnspecified; + + case AF_INET: + return ::Ipv4AddressType(reinterpret_cast(in_addr())); + + case AF_INET6: + return ::Ipv6AddressType(reinterpret_cast(in6_addr())); + } + + WI_ASSERT_MSG(false, "Unknown address family"); + return NlatInvalid; + } + + inline void socket_address::set_port(USHORT port) WI_NOEXCEPT + { + // port values in sockaddr's are always in network-byte order + USHORT port_network_byte_order = htons(port); + switch (family()) + { + case AF_INET: + m_sockaddr.Ipv4.sin_port = port_network_byte_order; + break; + + case AF_INET6: + m_sockaddr.Ipv6.sin6_port = port_network_byte_order; + break; + + default: + WI_ASSERT_MSG(false, "Unknown address family"); + } + } + + inline void socket_address::set_scope_id(ULONG scopeid) WI_NOEXCEPT + { + WI_ASSERT(family() == AF_INET6); + if (family() == AF_INET6) + { + m_sockaddr.Ipv6.sin6_scope_id = scopeid; + } + } + + inline void socket_address::set_flow_info(ULONG flowinfo) WI_NOEXCEPT + { + WI_ASSERT(family() == AF_INET6); + if (family() == AF_INET6) + { + m_sockaddr.Ipv6.sin6_flowinfo = flowinfo; + } + } + + inline void socket_address::set_address_any() WI_NOEXCEPT + { + const auto original_family = family(); + switch (original_family) + { + case AF_UNSPEC: + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + break; + + case AF_INET: + { + auto original_port = m_sockaddr.Ipv4.sin_port; + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + m_sockaddr.Ipv4.sin_port = original_port; + break; + } + + case AF_INET6: + { + auto original_port = m_sockaddr.Ipv6.sin6_port; + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + m_sockaddr.Ipv6.sin6_port = original_port; + break; + } + + default: + FAIL_FAST_MSG("Unknown family (%d)", original_family); + } + + m_sockaddr.si_family = original_family; + } + + inline void socket_address::set_address_loopback() WI_NOEXCEPT + { + const auto original_family = family(); + switch (original_family) + { + case AF_INET: + { + auto original_port = m_sockaddr.Ipv4.sin_port; + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + m_sockaddr.Ipv4.sin_port = original_port; + m_sockaddr.Ipv4.sin_addr.s_addr = INADDR_LOOPBACK; + break; + } + + case AF_INET6: + { + auto original_port = m_sockaddr.Ipv6.sin6_port; + ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + m_sockaddr.Ipv6.sin6_port = original_port; + m_sockaddr.Ipv6.sin6_addr = IN6ADDR_LOOPBACK_INIT; + break; + } + + default: + FAIL_FAST_MSG("Unknown family to create a loopback socket address (%d)", original_family); + } + + m_sockaddr.si_family = original_family; + } + + inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT + { + WI_ASSERT(family() == AF_INET); + const auto original_port = m_sockaddr.Ipv4.sin_port; + reset(AF_INET); + m_sockaddr.Ipv4.sin_port = original_port; + m_sockaddr.Ipv4.sin_addr.s_addr = addr->s_addr; + } + + inline void socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT + { + WI_ASSERT(family() == AF_INET6); + const auto original_port = m_sockaddr.Ipv4.sin_port; + reset(AF_INET6); + m_sockaddr.Ipv6.sin6_port = original_port; + m_sockaddr.Ipv6.sin6_addr = *addr; + } + + inline HRESULT socket_address::set_address_nothrow(SOCKET s) WI_NOEXCEPT + { + auto nameLength = length(); + auto error = ::getsockname(s, sockaddr(), &nameLength); + if (error != 0) + { + error = ::WSAGetLastError(); + RETURN_WIN32(error); + } + return S_OK; + } + + inline HRESULT socket_address::set_address_nothrow(_In_ PCWSTR wszAddr) WI_NOEXCEPT + { + ADDRINFOW hints{}; + hints.ai_flags = AI_NUMERICHOST; + + ADDRINFOW* pResult{}; + const auto error = ::GetAddrInfoW(wszAddr, nullptr, &hints, &pResult); + if (0 == error) + { + set_sockaddr(pResult->ai_addr, pResult->ai_addrlen); + ::FreeAddrInfoW(pResult); + return S_OK; + } + RETURN_WIN32(error); + } + +// the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API +#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS + inline HRESULT socket_address::set_address_nothrow(_In_ PCSTR szAddr) WI_NOEXCEPT + { + ADDRINFOA hints{}; + hints.ai_flags = AI_NUMERICHOST; + + ADDRINFOA* pResult{}; + const auto error = ::GetAddrInfoA(szAddr, nullptr, &hints, &pResult) if (0 == error) + { + set_sockaddr(pResult->ai_addr, pResult->ai_addrlen); + FreeAddrInfoA(pResult); + return S_OK; + } + RETURN_WIN32(error); + } +#endif + +#if defined(_STRING_) || defined(WIL_DOXYGEN) + inline std::wstring socket_address::write_address() const + { + socket_address_wstring returnString{}; + write_address_nothrow(returnString); + returnString[INET6_ADDRSTRLEN - 1] = L'\0'; + return returnString; + } +#endif + + inline HRESULT socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + { + ::ZeroMemory(address, sizeof(socket_address_wstring)); + + const void* const pAddr = family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr); + // the last param to InetNtopW is # of characters, not bytes + return nullptr != InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN); + } + + inline HRESULT socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + { + ::ZeroMemory(address, sizeof(socket_address_string)); + + const void* const pAddr = family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr); + // the last param to InetNtopA is # of characters, not bytes + const auto* error_value = InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN); + if (error_value == nullptr) + { + const auto gle = ::WSAGetLastError(); + RETURN_WIN32(gle); + } + return S_OK; + } + +#if defined(_STRING_) || defined(WIL_DOXYGEN) + inline std::wstring socket_address::write_complete_address() const + { + socket_address_wstring returnString{}; + write_complete_address_nothrow(returnString); + returnString[INET6_ADDRSTRLEN - 1] = L'\0'; + return returnString; + } +#endif + + inline HRESULT socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + { + ::ZeroMemory(address, sizeof(socket_address_wstring)); + // addressLength == # of chars, not bytes + DWORD addressLength = INET6_ADDRSTRLEN; + if (::WSAAddressToStringW(const_cast(sockaddr()), c_sockaddr_size, nullptr, address, &addressLength) != 0) + { + const auto gle = ::WSAGetLastError(); + RETURN_WIN32(gle); + } + return S_OK; + } + +// the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API +#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS + inline HRESULT socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + { + ::ZeroMemory(address, sizeof(socket_address_string)); + DWORD addressLength = INET6_ADDRSTRLEN; + if (::WSAAddressToStringA(const_cast(sockaddr()), c_sockaddrSize, nullptr, address, &addressLength) != 0) + { + const auto gle = ::WSAGetLastError(); + RETURN_WIN32(gle); + } + return S_OK; + } +#endif + + inline ADDRESS_FAMILY socket_address::family() const WI_NOEXCEPT + { + return m_sockaddr.si_family; + } + + inline USHORT socket_address::port() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return 0; + + case AF_INET: + return ntohs(m_sockaddr.Ipv4.sin_port); + + case AF_INET6: + return ntohs(m_sockaddr.Ipv6.sin6_port); + + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return 0; + } + } + + inline ULONG socket_address::flow_info() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return 0; + + case AF_INET: + return 0; + + case AF_INET6: + return m_sockaddr.Ipv6.sin6_flowinfo; + + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return 0; + } + } + + inline ULONG socket_address::scope_id() const WI_NOEXCEPT + { + switch (family()) + { + case AF_UNSPEC: + return 0; + + case AF_INET: + return 0; + + case AF_INET6: + return m_sockaddr.Ipv6.sin6_scope_id; + + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return 0; + } + } + + inline SOCKADDR* socket_address::sockaddr() WI_NOEXCEPT + { + return reinterpret_cast(&m_sockaddr); + } + + inline SOCKADDR_IN* socket_address::sockaddr_in() WI_NOEXCEPT + { + return &m_sockaddr.Ipv4; + } + + inline SOCKADDR_IN6* socket_address::sockaddr_in6() WI_NOEXCEPT + { + return &m_sockaddr.Ipv6; + } + + inline SOCKADDR_INET* socket_address::sockaddr_inet() WI_NOEXCEPT + { + return &m_sockaddr; + } + + inline IN_ADDR* socket_address::in_addr() WI_NOEXCEPT + { + return &m_sockaddr.Ipv4.sin_addr; + } + + inline IN6_ADDR* socket_address::in6_addr() WI_NOEXCEPT + { + return &m_sockaddr.Ipv6.sin6_addr; + } + + inline const SOCKADDR* socket_address::sockaddr() const WI_NOEXCEPT + { + return reinterpret_cast(&m_sockaddr); + } + + inline const SOCKADDR_IN* socket_address::sockaddr_in() const WI_NOEXCEPT + { + return &m_sockaddr.Ipv4; + } + + inline const SOCKADDR_IN6* socket_address::sockaddr_in6() const WI_NOEXCEPT + { + return &m_sockaddr.Ipv6; + } + + inline const SOCKADDR_INET* socket_address::sockaddr_inet() const WI_NOEXCEPT + { + return &m_sockaddr; + } + + inline const IN_ADDR* socket_address::in_addr() const WI_NOEXCEPT + { + return &m_sockaddr.Ipv4.sin_addr; + } + + inline const IN6_ADDR* socket_address::in6_addr() const WI_NOEXCEPT + { + return &m_sockaddr.Ipv6.sin6_addr; + } +} // namespace sockets +} // namespace wil + +#endif From c3480411e00fa094961925a7f2b1ca032794380d Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sun, 27 Oct 2024 23:13:52 -0700 Subject: [PATCH 02/22] adding networking tests --- include/wil/sockets.h | 80 ++++++--- tests/SocketTests.cpp | 407 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 467 insertions(+), 20 deletions(-) create mode 100644 tests/SocketTests.cpp diff --git a/include/wil/sockets.h b/include/wil/sockets.h index eb725f5b..bc7f13ae 100644 --- a/include/wil/sockets.h +++ b/include/wil/sockets.h @@ -23,7 +23,7 @@ // define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* APIs #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) -#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid it included with Windows.h +#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid winsock.h included with Windows.h #endif #include @@ -40,6 +40,42 @@ namespace wil //! Functions and classes that support network sockets operations and structures namespace sockets { + //! A type that calls WSACleanup on destruction (or reset()). + using unique_wsacleanup_call = unique_call; + + //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed + WI_NODISCARD inline unique_wsacleanup_call WSAStartup_nothrow() + { + WSADATA unused_data{}; + const auto error = ::WSAStartup(WINSOCK_VERSION, &unused_data); + LOG_IF_WIN32_ERROR(error); + + unique_wsacleanup_call return_cleanup{}; + if (error != 0) + { + // internally set m_call to false + // so the caller can check the return object against its operator bool + // to determine if it succeeded + return_cleanup.release(); + } + return return_cleanup; + } + + //! Calls WSAStartup and fail-fasts if it fails; returns an RAII object that reverts + WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() + { + WSADATA unused_data{}; + FAIL_FAST_IF_FAILED(::WSAStartup(WINSOCK_VERSION, &unused_data)); + return {}; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + WI_NODISCARD inline unique_wsacleanup_call WSAStartup() + { + THROW_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); + return {}; + } +#endif // encapsulates working with the sockaddr datatype // @@ -59,28 +95,32 @@ namespace sockets // Commonly used sockaddr* types that are using with TCPIP networking: // // sockaddr_storage / SOCKADDR_STORAGE - // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address + // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address // // sockaddr_in / SOCKADDR_IN - // - a sockaddr* derived type designed to contain an IPv4 address and port number + // - a sockaddr* derived type designed to contain an IPv4 address and port number // sockaddr_in6 / SOCKADDR_IN6 - // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info + // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info // SOCKADDR_INET - // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address + // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address // // in_addr (IN_ADDR) - // - the raw address portion of a sockaddr_in + // - the raw address portion of a sockaddr_in // in6_addr (IN6_ADDR) - // - the raw address portion of a sockaddr_in6 + // - the raw address portion of a sockaddr_in6 // // SOCKET_ADDRESS - // - not a derived sockaddr* type - // - a structure containing both a sockaddr* and its length fields + // - not a derived sockaddr* type + // - a structure containing both a sockaddr* and its length fields // // SOCKADDR_DL... data-link class addr_info; addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; + + // class addr_info encapsulates the ADDRINFO structure + // this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo + // exposes iterator semantics to safely access these addresses class addr_info { public: @@ -92,7 +132,7 @@ namespace sockets iterator begin(); iterator end(); - [[nodiscard]] int last_error() const WI_NOEXCEPT + [[nodiscard]] int get_last_error() const WI_NOEXCEPT { return m_lastError; } @@ -136,11 +176,11 @@ namespace sockets int m_lastError{}; }; - inline addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT + inline ::wil::sockets::addr_info resolve_name_nothrow(_In_ PCWSTR name, const ADDRINFOW* addrInfoHints = nullptr) WI_NOEXCEPT { - ADDRINFOW* addrResult{}; int lastError = 0; - if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) + ADDRINFOW* addrResult{}; + if (0 != ::GetAddrInfoW(name, nullptr, addrInfoHints, &addrResult)) { lastError = ::WSAGetLastError(); } @@ -162,8 +202,8 @@ namespace sockets explicit socket_address(const SOCKADDR_IN6*) WI_NOEXCEPT; explicit socket_address(const SOCKADDR_INET*) WI_NOEXCEPT; explicit socket_address(const SOCKET_ADDRESS*) WI_NOEXCEPT; - explicit socket_address(const IN_ADDR*, uint16_t port = 0) WI_NOEXCEPT; - explicit socket_address(const IN6_ADDR*, uint16_t port = 0) WI_NOEXCEPT; + explicit socket_address(const IN_ADDR*, unsigned short port = 0) WI_NOEXCEPT; + explicit socket_address(const IN6_ADDR*, unsigned short port = 0) WI_NOEXCEPT; ~socket_address() = default; socket_address(const socket_address&) WI_NOEXCEPT = default; @@ -504,7 +544,7 @@ namespace sockets inline void socket_address::set_port(USHORT port) WI_NOEXCEPT { - // port values in sockaddr's are always in network-byte order + // port values in a sockaddr are always in network-byte order USHORT port_network_byte_order = htons(port); switch (family()) { @@ -521,21 +561,21 @@ namespace sockets } } - inline void socket_address::set_scope_id(ULONG scopeid) WI_NOEXCEPT + inline void socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); if (family() == AF_INET6) { - m_sockaddr.Ipv6.sin6_scope_id = scopeid; + m_sockaddr.Ipv6.sin6_scope_id = scopeId; } } - inline void socket_address::set_flow_info(ULONG flowinfo) WI_NOEXCEPT + inline void socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); if (family() == AF_INET6) { - m_sockaddr.Ipv6.sin6_flowinfo = flowinfo; + m_sockaddr.Ipv6.sin6_flowinfo = flowInfo; } } diff --git a/tests/SocketTests.cpp b/tests/SocketTests.cpp new file mode 100644 index 00000000..97e21a9f --- /dev/null +++ b/tests/SocketTests.cpp @@ -0,0 +1,407 @@ +#include "pch.h" + +#include + +#include "common.h" + +constexpr auto* Test_in_addr_string = L"1.1.1.1"; +in_addr Test_in_addr{}; +constexpr auto* Test_in_addr_string2 = L"1.1.1.2"; +in_addr Test_in_addr2{}; +constexpr auto* Test_in6_addr_string = L"2001::1:1:1:1"; +in6_addr Test_in6_addr{}; +constexpr auto* Test_in6_addr_string2 = L"2001::1:1:1:2"; +in6_addr Test_in6_addr2{}; + +constexpr auto* Test_linklocal_in_addr_string = L"169.254.111.222"; +in_addr Test_linklocal_in_addr{}; +constexpr auto* Test_linklocal_in6_addr_string = L"fe80::1:1:1:1"; +in6_addr Test_linklocal_in6_addr{}; + +constexpr auto* Test_any_in_addr_string = L"0.0.0.0"; +in_addr Test_any_in_addr{}; +constexpr auto* Test_any_in6_addr_string = L"::"; +in6_addr Test_any_in6_addr{}; + +constexpr uint16_t TestPort = 12345; + +INIT_ONCE SocketTestInit{INIT_ONCE_STATIC_INIT}; +void InitTestAddresses() +{ + InitOnceExecuteOnce( + &SocketTestInit, + [](PINIT_ONCE, PVOID, PVOID*) -> BOOL { + LPCWSTR terminator{}; + auto error = RtlIpv4StringToAddressW(Test_in_addr_string, TRUE, &terminator, &Test_in_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + error = RtlIpv4StringToAddressW(Test_in_addr_string2, TRUE, &terminator, &Test_in_addr2); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + error = RtlIpv6StringToAddressW(Test_in6_addr_string, &terminator, &Test_in6_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + error = RtlIpv6StringToAddressW(Test_in6_addr_string2, &terminator, &Test_in6_addr2); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + error = RtlIpv4StringToAddressW(Test_linklocal_in_addr_string, TRUE, &terminator, &Test_linklocal_in_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + error = RtlIpv6StringToAddressW(Test_linklocal_in6_addr_string, &terminator, &Test_linklocal_in6_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + return TRUE; + }, + nullptr, + nullptr); +} + +TEST_CASE("SocketTests::Verifying_in_addr_interactions", "[sockets]") +{ + InitTestAddresses(); + REQUIRE(wil::sockets::socket_address::length() == sizeof(SOCKADDR_INET)); + + wil::sockets::socket_address default_addr{}; + + wil::sockets::socket_address test_v4_addr{&Test_in_addr}; + wil::sockets::socket_address test_v4_addr2{&Test_in_addr2}; + wil::sockets::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; + + wil::sockets::socket_address test_v6_addr{&Test_in6_addr}; + wil::sockets::socket_address test_v6_addr2{&Test_in6_addr2}; + wil::sockets::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; + + wil::sockets::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; + wil::sockets::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; + + wil::sockets::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; + wil::sockets::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; + + wil::sockets::socket_address test_v4_any_addr{&Test_any_in_addr}; + wil::sockets::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; + + wil::sockets::socket_address test_v6_any_addr{&Test_any_in6_addr}; + wil::sockets::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + + SECTION("Default socket address properties") + { + REQUIRE(default_addr.family() == AF_UNSPEC); + REQUIRE(!default_addr.is_address_any()); + REQUIRE(!default_addr.is_address_linklocal()); + REQUIRE(!default_addr.is_address_loopback()); + REQUIRE(NlatUnspecified == default_addr.get_address_type()); + + REQUIRE(default_addr == default_addr); + } + + SECTION("IPv4 in_addr properties") + { + REQUIRE(test_v4_addr.family() == AF_INET); + REQUIRE(!test_v4_addr.is_address_any()); + REQUIRE(!test_v4_addr.is_address_linklocal()); + REQUIRE(!test_v4_addr.is_address_loopback()); + REQUIRE(NlatUnicast == test_v4_addr.get_address_type()); + REQUIRE(NlatUnicast == test_v4_addr2.get_address_type()); + + REQUIRE(test_v4_addr.in_addr()->s_addr == Test_in_addr.s_addr); + REQUIRE(test_v4_addr2.in_addr()->s_addr == Test_in_addr2.s_addr); + REQUIRE(test_v4_addr.port() == 0); + REQUIRE(test_v4_addr.scope_id() == 0); + REQUIRE(test_v4_addr.flow_info() == 0); + + REQUIRE(test_v4_addr == test_v4_addr); + REQUIRE(test_v4_addr != test_v4_addr2); + REQUIRE(test_v4_addr < test_v4_addr2); + REQUIRE(test_v4_addr2 > test_v4_addr); + REQUIRE(test_v4_addr != default_addr); + REQUIRE(test_v4_addr > default_addr); + } + + SECTION("IPv4 in_addr with port properties") + { + REQUIRE(test_v4_addr_with_port.family() == AF_INET); + REQUIRE(!test_v4_addr_with_port.is_address_any()); + REQUIRE(!test_v4_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v4_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnicast == test_v4_addr_with_port.get_address_type()); + + REQUIRE(test_v4_addr_with_port.in_addr()->s_addr == Test_in_addr.s_addr); + REQUIRE(test_v4_addr_with_port.in_addr()->s_addr == test_v4_addr.in_addr()->s_addr); + REQUIRE(test_v4_addr_with_port.port() == TestPort); + REQUIRE(test_v4_addr_with_port.scope_id() == 0); + REQUIRE(test_v4_addr_with_port.flow_info() == 0); + + REQUIRE(test_v4_addr_with_port == test_v4_addr_with_port); + REQUIRE(test_v4_addr_with_port != default_addr); + REQUIRE(test_v4_addr_with_port != test_v4_addr); + REQUIRE(test_v4_addr_with_port > test_v4_addr); + REQUIRE(test_v4_addr_with_port > test_v4_addr2); + REQUIRE(test_v4_addr_with_port > default_addr); + } + + SECTION("IPv6 in6_addr properties") + { + REQUIRE(test_v6_addr.family() == AF_INET6); + REQUIRE(!test_v6_addr.is_address_any()); + REQUIRE(!test_v6_addr.is_address_linklocal()); + REQUIRE(!test_v6_addr.is_address_loopback()); + REQUIRE(NlatUnicast == test_v6_addr.get_address_type()); + REQUIRE(NlatUnicast == test_v6_addr2.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_addr.in6_addr(), &Test_in6_addr, sizeof(in6_addr))); + REQUIRE(0 == memcmp(test_v6_addr2.in6_addr(), &Test_in6_addr2, sizeof(in6_addr))); + REQUIRE(test_v6_addr.port() == 0); + REQUIRE(test_v6_addr.scope_id() == 0); + REQUIRE(test_v6_addr.flow_info() == 0); + + REQUIRE(test_v6_addr == test_v6_addr); + REQUIRE(test_v6_addr != test_v6_addr2); + REQUIRE(test_v6_addr < test_v6_addr2); + REQUIRE(test_v6_addr2 > test_v6_addr); + REQUIRE(test_v6_addr != test_v4_addr); + REQUIRE(test_v6_addr > test_v4_addr); + REQUIRE(test_v6_addr != default_addr); + REQUIRE(test_v6_addr > default_addr); + } + + SECTION("IPv6 in6_addr with port properties") + { + REQUIRE(test_v6_addr_with_port.family() == AF_INET6); + REQUIRE(!test_v6_addr_with_port.is_address_any()); + REQUIRE(!test_v6_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v6_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnicast == test_v6_addr_with_port.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_addr_with_port.in6_addr(), &Test_in6_addr, sizeof(in6_addr))); + REQUIRE(0 == memcmp(test_v6_addr_with_port.in6_addr(), test_v6_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(test_v6_addr_with_port.port() == TestPort); + REQUIRE(test_v6_addr_with_port.scope_id() == 0); + REQUIRE(test_v6_addr_with_port.flow_info() == 0); + + REQUIRE(test_v6_addr_with_port == test_v6_addr_with_port); + REQUIRE(test_v6_addr_with_port != test_v4_addr); + REQUIRE(test_v6_addr_with_port != test_v4_addr_with_port); + REQUIRE(test_v6_addr_with_port != test_v6_addr); + REQUIRE(test_v6_addr_with_port != test_v6_addr2); + REQUIRE(test_v6_addr_with_port > test_v6_addr); + REQUIRE(test_v6_addr_with_port > test_v6_addr2); + REQUIRE(test_v6_addr_with_port != default_addr); + REQUIRE(test_v6_addr_with_port > default_addr); + } + + SECTION("IPv4 link-local in_addr properties") + { + REQUIRE(test_v4_linklocal_addr.family() == AF_INET); + REQUIRE(!test_v4_linklocal_addr.is_address_any()); + REQUIRE(test_v4_linklocal_addr.is_address_linklocal()); + REQUIRE(!test_v4_linklocal_addr.is_address_loopback()); + REQUIRE(NlatUnicast == test_v4_linklocal_addr.get_address_type()); + + REQUIRE(test_v4_linklocal_addr.in_addr()->s_addr == Test_linklocal_in_addr.s_addr); + REQUIRE(test_v4_linklocal_addr.port() == 0); + REQUIRE(test_v4_linklocal_addr.scope_id() == 0); + REQUIRE(test_v4_linklocal_addr.flow_info() == 0); + + REQUIRE(test_v4_linklocal_addr == test_v4_linklocal_addr); + REQUIRE(test_v4_linklocal_addr != default_addr); + REQUIRE(test_v4_linklocal_addr != test_v4_addr); + REQUIRE(test_v4_linklocal_addr != test_v4_addr_with_port); + REQUIRE(test_v4_linklocal_addr != test_v6_addr); + REQUIRE(test_v4_linklocal_addr != test_v6_addr_with_port); + } + + SECTION("IPv4 link-local in_addr with port properties") + { + REQUIRE(test_v4_linklocal_addr_with_port.family() == AF_INET); + REQUIRE(!test_v4_linklocal_addr_with_port.is_address_any()); + REQUIRE(test_v4_linklocal_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v4_linklocal_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnicast == test_v4_linklocal_addr_with_port.get_address_type()); + + REQUIRE(test_v4_linklocal_addr_with_port.in_addr()->s_addr == Test_linklocal_in_addr.s_addr); + REQUIRE(test_v4_linklocal_addr_with_port.in_addr()->s_addr == test_v4_linklocal_addr.in_addr()->s_addr); + REQUIRE(test_v4_linklocal_addr_with_port.port() == TestPort); + REQUIRE(test_v4_linklocal_addr_with_port.scope_id() == 0); + REQUIRE(test_v4_linklocal_addr_with_port.flow_info() == 0); + + REQUIRE(test_v4_linklocal_addr_with_port == test_v4_linklocal_addr_with_port); + REQUIRE(test_v4_linklocal_addr_with_port != default_addr); + REQUIRE(test_v4_linklocal_addr_with_port != test_v4_addr); + REQUIRE(test_v4_linklocal_addr_with_port != test_v4_addr_with_port); + REQUIRE(test_v4_linklocal_addr_with_port != test_v6_addr); + REQUIRE(test_v4_linklocal_addr_with_port != test_v6_addr_with_port); + REQUIRE(test_v4_linklocal_addr_with_port != test_v4_linklocal_addr); + } + + SECTION("IPv6 link-local in6_addr properties") + { + REQUIRE(test_v6_linklocal_addr.family() == AF_INET6); + REQUIRE(!test_v6_linklocal_addr.is_address_any()); + REQUIRE(test_v6_linklocal_addr.is_address_linklocal()); + REQUIRE(!test_v6_linklocal_addr.is_address_loopback()); + REQUIRE(NlatUnicast == test_v6_linklocal_addr.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_linklocal_addr.in6_addr(), &Test_linklocal_in6_addr, sizeof(in6_addr))); + REQUIRE(test_v6_linklocal_addr.port() == 0); + REQUIRE(test_v6_linklocal_addr.scope_id() == 0); + REQUIRE(test_v6_linklocal_addr.flow_info() == 0); + + REQUIRE(test_v6_linklocal_addr == test_v6_linklocal_addr); + REQUIRE(test_v6_linklocal_addr != default_addr); + REQUIRE(test_v6_linklocal_addr != test_v4_addr); + REQUIRE(test_v6_linklocal_addr != test_v4_addr_with_port); + REQUIRE(test_v6_linklocal_addr != test_v6_addr); + REQUIRE(test_v6_linklocal_addr != test_v6_addr_with_port); + REQUIRE(test_v6_linklocal_addr != test_v4_linklocal_addr); + REQUIRE(test_v6_linklocal_addr != test_v4_linklocal_addr_with_port); + } + + SECTION("IPv6 link-local in6_addr with port properties") + { + REQUIRE(test_v6_linklocal_addr_with_port.family() == AF_INET6); + REQUIRE(!test_v6_linklocal_addr_with_port.is_address_any()); + REQUIRE(test_v6_linklocal_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v6_linklocal_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnicast == test_v6_linklocal_addr_with_port.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_linklocal_addr_with_port.in6_addr(), &Test_linklocal_in6_addr, sizeof(in6_addr))); + REQUIRE(0 == memcmp(test_v6_linklocal_addr_with_port.in6_addr(), test_v6_linklocal_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(test_v6_linklocal_addr_with_port.port() == TestPort); + REQUIRE(test_v6_linklocal_addr_with_port.scope_id() == 0); + REQUIRE(test_v6_linklocal_addr_with_port.flow_info() == 0); + + REQUIRE(test_v6_linklocal_addr_with_port == test_v6_linklocal_addr_with_port); + REQUIRE(test_v6_linklocal_addr_with_port != default_addr); + REQUIRE(test_v6_linklocal_addr_with_port != test_v4_addr); + REQUIRE(test_v6_linklocal_addr_with_port != test_v4_addr_with_port); + REQUIRE(test_v6_linklocal_addr_with_port != test_v6_addr); + REQUIRE(test_v6_linklocal_addr_with_port != test_v6_addr_with_port); + REQUIRE(test_v6_linklocal_addr_with_port != test_v4_linklocal_addr); + REQUIRE(test_v6_linklocal_addr_with_port != test_v4_linklocal_addr_with_port); + REQUIRE(test_v6_linklocal_addr_with_port != test_v6_linklocal_addr); + } + + SECTION("IPv4 any-addr in_addr properties") + { + REQUIRE(test_v4_any_addr.family() == AF_INET); + REQUIRE(test_v4_any_addr.is_address_any()); + REQUIRE(!test_v4_any_addr.is_address_linklocal()); + REQUIRE(!test_v4_any_addr.is_address_loopback()); + REQUIRE(NlatUnspecified == test_v4_any_addr.get_address_type()); + + REQUIRE(test_v4_any_addr.in_addr()->s_addr == Test_any_in_addr.s_addr); + REQUIRE(test_v4_any_addr.port() == 0); + REQUIRE(test_v4_any_addr.scope_id() == 0); + REQUIRE(test_v4_any_addr.flow_info() == 0); + + REQUIRE(test_v4_any_addr == test_v4_any_addr); + REQUIRE(test_v4_any_addr != default_addr); + REQUIRE(test_v4_any_addr != test_v4_addr); + REQUIRE(test_v4_any_addr != test_v4_addr_with_port); + REQUIRE(test_v4_any_addr != test_v6_addr); + REQUIRE(test_v4_any_addr != test_v6_addr_with_port); + REQUIRE(test_v4_any_addr != test_v4_linklocal_addr); + REQUIRE(test_v4_any_addr != test_v4_linklocal_addr_with_port); + REQUIRE(test_v4_any_addr != test_v6_linklocal_addr); + REQUIRE(test_v4_any_addr != test_v6_linklocal_addr_with_port); + } + + SECTION("IPv4 any-addr in_addr with port properties") + { + REQUIRE(test_v4_any_addr_with_port.family() == AF_INET); + REQUIRE(test_v4_any_addr_with_port.is_address_any()); + REQUIRE(!test_v4_any_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v4_any_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnspecified == test_v4_any_addr_with_port.get_address_type()); + + REQUIRE(test_v4_any_addr_with_port.in_addr()->s_addr == Test_any_in_addr.s_addr); + REQUIRE(test_v4_any_addr_with_port.in_addr()->s_addr == test_v4_any_addr.in_addr()->s_addr); + REQUIRE(test_v4_any_addr_with_port.port() == TestPort); + REQUIRE(test_v4_any_addr_with_port.scope_id() == 0); + REQUIRE(test_v4_any_addr_with_port.flow_info() == 0); + + REQUIRE(test_v4_any_addr_with_port == test_v4_any_addr_with_port); + REQUIRE(test_v4_any_addr_with_port != default_addr); + REQUIRE(test_v4_any_addr_with_port != test_v4_addr); + REQUIRE(test_v4_any_addr_with_port != test_v4_addr_with_port); + REQUIRE(test_v4_any_addr_with_port != test_v6_addr); + REQUIRE(test_v4_any_addr_with_port != test_v6_addr_with_port); + REQUIRE(test_v4_any_addr_with_port != test_v4_linklocal_addr); + REQUIRE(test_v4_any_addr_with_port != test_v4_linklocal_addr_with_port); + REQUIRE(test_v4_any_addr_with_port != test_v6_linklocal_addr); + REQUIRE(test_v4_any_addr_with_port != test_v6_linklocal_addr_with_port); + REQUIRE(test_v4_any_addr_with_port != test_v4_any_addr); + } + + SECTION("IPv6 any-addr in6_addr properties") + { + REQUIRE(test_v6_any_addr.family() == AF_INET6); + REQUIRE(test_v6_any_addr.is_address_any()); + REQUIRE(!test_v6_any_addr.is_address_linklocal()); + REQUIRE(!test_v6_any_addr.is_address_loopback()); + REQUIRE(NlatUnspecified == test_v6_any_addr.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_any_addr.in6_addr(), &Test_any_in6_addr, sizeof(in6_addr))); + REQUIRE(test_v6_any_addr.port() == 0); + REQUIRE(test_v6_any_addr.scope_id() == 0); + REQUIRE(test_v6_any_addr.flow_info() == 0); + + REQUIRE(test_v6_any_addr == test_v6_any_addr); + REQUIRE(test_v6_any_addr != default_addr); + REQUIRE(test_v6_any_addr != test_v4_addr); + REQUIRE(test_v6_any_addr != test_v4_addr_with_port); + REQUIRE(test_v6_any_addr != test_v6_addr); + REQUIRE(test_v6_any_addr != test_v6_addr_with_port); + REQUIRE(test_v6_any_addr != test_v4_linklocal_addr); + REQUIRE(test_v6_any_addr != test_v4_linklocal_addr_with_port); + REQUIRE(test_v6_any_addr != test_v6_linklocal_addr); + REQUIRE(test_v6_any_addr != test_v6_linklocal_addr_with_port); + REQUIRE(test_v6_any_addr != test_v4_any_addr); + REQUIRE(test_v6_any_addr != test_v4_any_addr_with_port); + } + + SECTION("IPv6 any-addr in6_addr with port properties") + { + REQUIRE(test_v6_any_addr_with_port.family() == AF_INET6); + REQUIRE(test_v6_any_addr_with_port.is_address_any()); + REQUIRE(!test_v6_any_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v6_any_addr_with_port.is_address_loopback()); + REQUIRE(NlatUnspecified == test_v6_any_addr_with_port.get_address_type()); + + REQUIRE(0 == memcmp(test_v6_any_addr_with_port.in6_addr(), &Test_any_in6_addr, sizeof(in6_addr))); + REQUIRE(0 == memcmp(test_v6_any_addr_with_port.in6_addr(), test_v6_any_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(test_v6_any_addr_with_port.port() == TestPort); + REQUIRE(test_v6_any_addr_with_port.scope_id() == 0); + REQUIRE(test_v6_any_addr_with_port.flow_info() == 0); + + REQUIRE(test_v6_any_addr_with_port == test_v6_any_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != default_addr); + REQUIRE(test_v6_any_addr_with_port != test_v4_addr); + REQUIRE(test_v6_any_addr_with_port != test_v4_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != test_v6_addr); + REQUIRE(test_v6_any_addr_with_port != test_v6_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != test_v4_linklocal_addr); + REQUIRE(test_v6_any_addr_with_port != test_v4_linklocal_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != test_v6_linklocal_addr); + REQUIRE(test_v6_any_addr_with_port != test_v6_linklocal_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != test_v4_any_addr); + REQUIRE(test_v6_any_addr_with_port != test_v4_any_addr_with_port); + REQUIRE(test_v6_any_addr_with_port != test_v6_any_addr); + } +} From 90e420cc205017dab69e9b73333f731ac3aeb899 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sun, 27 Oct 2024 23:18:09 -0700 Subject: [PATCH 03/22] Renamed to networking.h --- include/wil/{sockets.h => networking.h} | 12 ++++++------ tests/{SocketTests.cpp => NetworkingTests.cpp} | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) rename include/wil/{sockets.h => networking.h} (96%) rename tests/{SocketTests.cpp => NetworkingTests.cpp} (99%) diff --git a/include/wil/sockets.h b/include/wil/networking.h similarity index 96% rename from include/wil/sockets.h rename to include/wil/networking.h index bc7f13ae..24a6a8a1 100644 --- a/include/wil/sockets.h +++ b/include/wil/networking.h @@ -11,8 +11,8 @@ //! @file //! Helpers for using BSD sockets and Windows Winsock APIs and structures. //! Does not require the use of the STL or C++ exceptions (see _nothrow functions) -#ifndef __WIL_SOCKETS_INCLUDED -#define __WIL_SOCKETS_INCLUDED +#ifndef __WIL_NETWORKING_INCLUDED +#define __WIL_NETWORKING_INCLUDED #ifdef _KERNEL_MODE #error This header is not supported in kernel-mode. @@ -37,8 +37,8 @@ namespace wil { -//! Functions and classes that support network sockets operations and structures -namespace sockets +//! Functions and classes that support networking operations and structures +namespace networking { //! A type that calls WSACleanup on destruction (or reset()). using unique_wsacleanup_call = unique_call; @@ -176,7 +176,7 @@ namespace sockets int m_lastError{}; }; - inline ::wil::sockets::addr_info resolve_name_nothrow(_In_ PCWSTR name, const ADDRINFOW* addrInfoHints = nullptr) WI_NOEXCEPT + inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name, const ADDRINFOW* addrInfoHints = nullptr) WI_NOEXCEPT { int lastError = 0; ADDRINFOW* addrResult{}; @@ -900,7 +900,7 @@ namespace sockets { return &m_sockaddr.Ipv6.sin6_addr; } -} // namespace sockets +} // namespace networking } // namespace wil #endif diff --git a/tests/SocketTests.cpp b/tests/NetworkingTests.cpp similarity index 99% rename from tests/SocketTests.cpp rename to tests/NetworkingTests.cpp index 97e21a9f..531f67c0 100644 --- a/tests/SocketTests.cpp +++ b/tests/NetworkingTests.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include +#include #include "common.h" From 0007fba840c45a2aa2a7e46d0a7a0a7e12bf9ccd Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Mon, 28 Oct 2024 00:00:23 -0700 Subject: [PATCH 04/22] Updates --- include/wil/networking.h | 42 +++++++++++++++++++++++----------- tests/NetworkingTests.cpp | 37 ++++++++++++++++++++++++++++++ tests/cpplatest/CMakeLists.txt | 3 ++- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index 24a6a8a1..0b03e1d7 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -72,6 +72,7 @@ namespace networking #ifdef WIL_ENABLE_EXCEPTIONS WI_NODISCARD inline unique_wsacleanup_call WSAStartup() { + WSADATA unused_data{}; THROW_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); return {}; } @@ -168,6 +169,10 @@ namespace networking private: friend addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; +#ifdef WIL_ENABLE_EXCEPTIONS + friend addr_info resolve_name(_In_ PCWSTR name); +#endif + addr_info(_In_ ADDRINFOW* addrResult, int error) : m_addrResult{addrResult}, m_lastError{error} { } @@ -176,11 +181,11 @@ namespace networking int m_lastError{}; }; - inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name, const ADDRINFOW* addrInfoHints = nullptr) WI_NOEXCEPT + inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT { int lastError = 0; ADDRINFOW* addrResult{}; - if (0 != ::GetAddrInfoW(name, nullptr, addrInfoHints, &addrResult)) + if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) { lastError = ::WSAGetLastError(); } @@ -188,6 +193,26 @@ namespace networking return {addrResult, lastError}; } + inline ::wil::networking::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT + { + return ::wil::networking::resolve_name_nothrow(L""); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline ::wil::networking::addr_info resolve_name(_In_ PCWSTR name) + { + ADDRINFOW* addrResult{}; + THROW_IF_WIN32_ERROR(::GetAddrInfoW(name, nullptr, nullptr, &addrResult)); + return {addrResult, 0}; + } + + inline ::wil::networking::addr_info resolve_local_addresses() WI_NOEXCEPT + { + return ::wil::networking::resolve_name(L""); + } + +#endif + // INET6_ADDRSTRLEN is guaranteed to be larger than INET_ADDRSTRLEN for IPv4 addresses typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; @@ -790,13 +815,10 @@ namespace networking { case AF_UNSPEC: return 0; - case AF_INET: return ntohs(m_sockaddr.Ipv4.sin_port); - case AF_INET6: return ntohs(m_sockaddr.Ipv6.sin6_port); - default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; @@ -808,14 +830,11 @@ namespace networking switch (family()) { case AF_UNSPEC: - return 0; - + // fallthrough case AF_INET: return 0; - case AF_INET6: return m_sockaddr.Ipv6.sin6_flowinfo; - default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; @@ -827,14 +846,11 @@ namespace networking switch (family()) { case AF_UNSPEC: - return 0; - + // fallthrough case AF_INET: return 0; - case AF_INET6: return m_sockaddr.Ipv6.sin6_scope_id; - default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 531f67c0..102a8ea4 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -72,6 +72,43 @@ void InitTestAddresses() nullptr); } +TEST_CASE("SocketTests::Verifying_wsastartup_cleanup", "[sockets]") +{ + // verify socket APIs fail without having called WSAStartup + const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + const auto gle = ::WSAGetLastError(); + REQUIRE(socket_test == INVALID_SOCKET); + REQUIRE(gle == WSANOTINITIALISED); + + SECTION("Verifying _nothrow") + { + const auto cleanup = wil::networking::WSAStartup_nothrow(); + const auto bool succeeded = socket_test; + REQUIRE(succeeded); + const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + REQUIRE(socket_test != INVALID_SOCKET); + ::closesocket(socket_test); + } + + SECTION("Verifying _failfast") + { + const auto cleanup = wil::networking::WSAStartup_failfast(); + const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + REQUIRE(socket_test != INVALID_SOCKET); + ::closesocket(socket_test); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + SECTION("Verifying throwing") + { + const auto cleanup = wil::networking::WSAStartup(); + const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + REQUIRE(socket_test != INVALID_SOCKET); + ::closesocket(socket_test); + } +#endif +} + TEST_CASE("SocketTests::Verifying_in_addr_interactions", "[sockets]") { InitTestAddresses(); diff --git a/tests/cpplatest/CMakeLists.txt b/tests/cpplatest/CMakeLists.txt index 06088169..e16f661b 100644 --- a/tests/cpplatest/CMakeLists.txt +++ b/tests/cpplatest/CMakeLists.txt @@ -26,11 +26,12 @@ endif() target_sources(witest.cpplatest PRIVATE ${COMMON_SOURCES} ${COROUTINE_SOURCES} - ${CMAKE_CURRENT_SOURCE_DIR}/../RegistryTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../CoroutineTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTAuthoringTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../NetworkingTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../RegistryTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp From 98196a0825692b23d496b38167cd44eed1439f94 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Tue, 29 Oct 2024 23:48:39 -0700 Subject: [PATCH 05/22] Currently added tests are building and passing --- include/wil/networking.h | 7 ++--- tests/CMakeLists.txt | 2 +- tests/NetworkingTests.cpp | 48 ++++++++++++++++++++++------------- tests/noexcept/CMakeLists.txt | 1 + tests/normal/CMakeLists.txt | 1 + tests/win7/CMakeLists.txt | 1 + 6 files changed, 38 insertions(+), 22 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index 0b03e1d7..fa89cbbb 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -23,7 +23,7 @@ // define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* APIs #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) -#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid winsock.h included with Windows.h +#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid winsock.h included with windows.h #endif #include @@ -65,7 +65,7 @@ namespace networking WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() { WSADATA unused_data{}; - FAIL_FAST_IF_FAILED(::WSAStartup(WINSOCK_VERSION, &unused_data)); + FAIL_FAST_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); return {}; } @@ -655,7 +655,8 @@ namespace networking auto original_port = m_sockaddr.Ipv6.sin6_port; ::ZeroMemory(&m_sockaddr, c_sockaddr_size); m_sockaddr.Ipv6.sin6_port = original_port; - m_sockaddr.Ipv6.sin6_addr = IN6ADDR_LOOPBACK_INIT; + // some compilers will not allow assignment of IN6ADDR_LOOPBACK_INIT + IN6_SET_ADDR_LOOPBACK(&m_sockaddr.Ipv6.sin6_addr); break; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee8a87f8..e4e737cb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,7 +88,7 @@ find_package(Catch2 CONFIG REQUIRED) include_directories(${DETOURS_INCLUDE_DIRS}) add_definitions(-DNOMINMAX) -link_libraries(${DETOURS_LIBRARY} Catch2::Catch2WithMain) +link_libraries(${DETOURS_LIBRARY} Catch2::Catch2WithMain ws2_32.lib ntdll.lib) add_subdirectory(app) add_subdirectory(cpplatest) diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 102a8ea4..926dce40 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -25,7 +25,7 @@ in6_addr Test_any_in6_addr{}; constexpr uint16_t TestPort = 12345; -INIT_ONCE SocketTestInit{INIT_ONCE_STATIC_INIT}; +INIT_ONCE SocketTestInit = INIT_ONCE_STATIC_INIT; void InitTestAddresses() { InitOnceExecuteOnce( @@ -66,6 +66,18 @@ void InitTestAddresses() return FALSE; } + error = RtlIpv4StringToAddressW(Test_any_in_addr_string, TRUE, &terminator, &Test_any_in_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + error = RtlIpv6StringToAddressW(Test_any_in6_addr_string, &terminator, &Test_any_in6_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + return TRUE; }, nullptr, @@ -83,7 +95,7 @@ TEST_CASE("SocketTests::Verifying_wsastartup_cleanup", "[sockets]") SECTION("Verifying _nothrow") { const auto cleanup = wil::networking::WSAStartup_nothrow(); - const auto bool succeeded = socket_test; + const bool succeeded = !!cleanup; REQUIRE(succeeded); const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); REQUIRE(socket_test != INVALID_SOCKET); @@ -112,29 +124,29 @@ TEST_CASE("SocketTests::Verifying_wsastartup_cleanup", "[sockets]") TEST_CASE("SocketTests::Verifying_in_addr_interactions", "[sockets]") { InitTestAddresses(); - REQUIRE(wil::sockets::socket_address::length() == sizeof(SOCKADDR_INET)); + REQUIRE(wil::networking::socket_address::length() == sizeof(SOCKADDR_INET)); - wil::sockets::socket_address default_addr{}; + wil::networking::socket_address default_addr{}; - wil::sockets::socket_address test_v4_addr{&Test_in_addr}; - wil::sockets::socket_address test_v4_addr2{&Test_in_addr2}; - wil::sockets::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; + wil::networking::socket_address test_v4_addr{&Test_in_addr}; + wil::networking::socket_address test_v4_addr2{&Test_in_addr2}; + wil::networking::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; - wil::sockets::socket_address test_v6_addr{&Test_in6_addr}; - wil::sockets::socket_address test_v6_addr2{&Test_in6_addr2}; - wil::sockets::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; + wil::networking::socket_address test_v6_addr{&Test_in6_addr}; + wil::networking::socket_address test_v6_addr2{&Test_in6_addr2}; + wil::networking::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; - wil::sockets::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; - wil::sockets::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; + wil::networking::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; + wil::networking::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; - wil::sockets::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; - wil::sockets::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; + wil::networking::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; + wil::networking::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; - wil::sockets::socket_address test_v4_any_addr{&Test_any_in_addr}; - wil::sockets::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; + wil::networking::socket_address test_v4_any_addr{&Test_any_in_addr}; + wil::networking::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; - wil::sockets::socket_address test_v6_any_addr{&Test_any_in6_addr}; - wil::sockets::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; + wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; SECTION("Default socket address properties") { diff --git a/tests/noexcept/CMakeLists.txt b/tests/noexcept/CMakeLists.txt index b68b4467..6ff689cc 100644 --- a/tests/noexcept/CMakeLists.txt +++ b/tests/noexcept/CMakeLists.txt @@ -22,6 +22,7 @@ endif() target_sources(witest.noexcept PRIVATE ${COMMON_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/../NetworkingTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../RegistryTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp diff --git a/tests/normal/CMakeLists.txt b/tests/normal/CMakeLists.txt index 7b651c2f..6164e3cf 100644 --- a/tests/normal/CMakeLists.txt +++ b/tests/normal/CMakeLists.txt @@ -5,6 +5,7 @@ target_precompile_headers(witest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../pch.h) target_sources(witest PRIVATE ${COMMON_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/../NetworkingTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../RegistryTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp diff --git a/tests/win7/CMakeLists.txt b/tests/win7/CMakeLists.txt index 15445f9f..736cec8b 100644 --- a/tests/win7/CMakeLists.txt +++ b/tests/win7/CMakeLists.txt @@ -9,6 +9,7 @@ target_compile_definitions(witest.win7 PRIVATE target_sources(witest.win7 PRIVATE ${COMMON_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/../NetworkingTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../RegistryTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp From 2c7f2d8bd406af5986bbc78df6ed1fb260a113a8 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Wed, 30 Oct 2024 00:20:04 -0700 Subject: [PATCH 06/22] Adding more tests --- include/wil/networking.h | 39 +++++++++------ tests/NetworkingTests.cpp | 99 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 17 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index fa89cbbb..911b39fd 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -26,11 +26,12 @@ #error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid winsock.h included with windows.h #endif -#include +#include #include #include #include #include +#include // wil headers #include "resource.h" @@ -41,6 +42,9 @@ namespace wil namespace networking { //! A type that calls WSACleanup on destruction (or reset()). + //! WSAStartup must be called for the lifetime of all Winsock APIs (synchronous and asynchronous) + //! WSACleanup will unload the full Winsock catalog - all the libraries - with the final reference + //! which can lead to crashes if socket APIs are still being used after the final WSACleanup is called using unique_wsacleanup_call = unique_call; //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed @@ -267,7 +271,9 @@ namespace networking // set_address* preserves the existing address family and port in the object void set_address_any() WI_NOEXCEPT; + void set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT; void set_address_loopback() WI_NOEXCEPT; + void set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT; void set_address(const IN_ADDR*) WI_NOEXCEPT; void set_address(const IN6_ADDR*) WI_NOEXCEPT; [[nodiscard]] HRESULT set_address_nothrow(SOCKET) WI_NOEXCEPT; @@ -606,13 +612,13 @@ namespace networking inline void socket_address::set_address_any() WI_NOEXCEPT { - const auto original_family = family(); - switch (original_family) - { - case AF_UNSPEC: - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - break; + set_address_any(family()); + } + inline void socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT + { + switch (family) + { case AF_INET: { auto original_port = m_sockaddr.Ipv4.sin_port; @@ -630,16 +636,20 @@ namespace networking } default: - FAIL_FAST_MSG("Unknown family (%d)", original_family); + FAIL_FAST_MSG("Unknown family (%d)", family); } - m_sockaddr.si_family = original_family; + m_sockaddr.si_family = family; } inline void socket_address::set_address_loopback() WI_NOEXCEPT { - const auto original_family = family(); - switch (original_family) + set_address_loopback(family()); + } + + inline void socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT + { + switch (family) { case AF_INET: { @@ -653,18 +663,17 @@ namespace networking case AF_INET6: { auto original_port = m_sockaddr.Ipv6.sin6_port; - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - m_sockaddr.Ipv6.sin6_port = original_port; // some compilers will not allow assignment of IN6ADDR_LOOPBACK_INIT IN6_SET_ADDR_LOOPBACK(&m_sockaddr.Ipv6.sin6_addr); + m_sockaddr.Ipv6.sin6_port = original_port; break; } default: - FAIL_FAST_MSG("Unknown family to create a loopback socket address (%d)", original_family); + FAIL_FAST_MSG("Unknown family (%d)", family); } - m_sockaddr.si_family = original_family; + m_sockaddr.si_family = family; } inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 926dce40..169850e7 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -84,7 +84,7 @@ void InitTestAddresses() nullptr); } -TEST_CASE("SocketTests::Verifying_wsastartup_cleanup", "[sockets]") +TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") { // verify socket APIs fail without having called WSAStartup const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -121,7 +121,7 @@ TEST_CASE("SocketTests::Verifying_wsastartup_cleanup", "[sockets]") #endif } -TEST_CASE("SocketTests::Verifying_in_addr_interactions", "[sockets]") +TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") { InitTestAddresses(); REQUIRE(wil::networking::socket_address::length() == sizeof(SOCKADDR_INET)); @@ -454,3 +454,98 @@ TEST_CASE("SocketTests::Verifying_in_addr_interactions", "[sockets]") REQUIRE(test_v6_any_addr_with_port != test_v6_any_addr); } } + +TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") +{ + InitTestAddresses(); + REQUIRE(wil::networking::socket_address::length() == sizeof(SOCKADDR_INET)); + + wil::networking::socket_address default_addr{}; + + wil::networking::socket_address test_v4_addr{&Test_in_addr}; + wil::networking::socket_address test_v4_addr2{&Test_in_addr2}; + wil::networking::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; + + wil::networking::socket_address test_v6_addr{&Test_in6_addr}; + wil::networking::socket_address test_v6_addr2{&Test_in6_addr2}; + wil::networking::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; + + wil::networking::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; + wil::networking::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; + + wil::networking::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; + wil::networking::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; + + wil::networking::socket_address test_v4_any_addr{&Test_any_in_addr}; + wil::networking::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; + + wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; + wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + + SECTION("verify set_address_any") + { + wil::networking::socket_address v4_address; + v4_address.set_address_any(AF_INET); + REQUIRE(v4_address.family() == AF_INET); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.is_address_any()); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(!v4_address.is_address_loopback()); + REQUIRE(NlatUnspecified == v4_address.get_address_type()); + REQUIRE(v4_address == test_v4_any_addr); + + v4_address.set_port(TestPort); + REQUIRE(v4_address.family() == AF_INET); + REQUIRE(v4_address.port() == TestPort); + REQUIRE(v4_address.is_address_any()); + REQUIRE(v4_address == test_v4_any_addr_with_port); + + wil::networking::socket_address v6_address; + v6_address.set_address_any(AF_INET6); + REQUIRE(v6_address.family() == AF_INET6); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.is_address_any()); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(!v6_address.is_address_loopback()); + REQUIRE(NlatUnspecified == v6_address.get_address_type()); + REQUIRE(v6_address == test_v6_any_addr); + + v6_address.set_port(TestPort); + REQUIRE(v6_address.family() == AF_INET6); + REQUIRE(v6_address.port() == TestPort); + REQUIRE(v6_address.is_address_any()); + REQUIRE(v6_address == test_v6_any_addr_with_port); + + wil::networking::socket_address defaulted_v4_address{AF_INET}; + defaulted_v4_address.set_address_any(); + REQUIRE(defaulted_v4_address.family() == AF_INET); + REQUIRE(defaulted_v4_address.port() == 0); + REQUIRE(defaulted_v4_address.is_address_any()); + REQUIRE(!defaulted_v4_address.is_address_linklocal()); + REQUIRE(!defaulted_v4_address.is_address_loopback()); + REQUIRE(NlatUnspecified == defaulted_v4_address.get_address_type()); + REQUIRE(defaulted_v4_address == test_v4_any_addr); + + defaulted_v4_address.set_port(TestPort); + REQUIRE(defaulted_v4_address.family() == AF_INET); + REQUIRE(defaulted_v4_address.port() == TestPort); + REQUIRE(defaulted_v4_address.is_address_any()); + REQUIRE(defaulted_v4_address == test_v4_any_addr_with_port); + + wil::networking::socket_address defaulted_v6_address; + defaulted_v6_address.set_address_any(AF_INET6); + REQUIRE(defaulted_v6_address.family() == AF_INET6); + REQUIRE(defaulted_v6_address.port() == 0); + REQUIRE(defaulted_v6_address.is_address_any()); + REQUIRE(!defaulted_v6_address.is_address_linklocal()); + REQUIRE(!defaulted_v6_address.is_address_loopback()); + REQUIRE(NlatUnspecified == defaulted_v6_address.get_address_type()); + REQUIRE(defaulted_v6_address == test_v6_any_addr); + + defaulted_v6_address.set_port(TestPort); + REQUIRE(defaulted_v6_address.family() == AF_INET6); + REQUIRE(defaulted_v6_address.port() == TestPort); + REQUIRE(defaulted_v6_address.is_address_any()); + REQUIRE(defaulted_v6_address == test_v6_any_addr_with_port); + } +} \ No newline at end of file From 6a7d7691029c56959ba5072c51e5dd63b274b086 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Wed, 30 Oct 2024 00:22:56 -0700 Subject: [PATCH 07/22] more tests --- tests/NetworkingTests.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 169850e7..b4b8f190 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -500,6 +500,13 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address.is_address_any()); REQUIRE(v4_address == test_v4_any_addr_with_port); + // verify changing families + v4_address.set_address_any(AF_INET6); + REQUIRE(v4_address.family() == AF_INET6); + REQUIRE(v4_address.port() == TestPort); // TODO: I'm seriously debating having set_address* clear the port + REQUIRE(v4_address.is_address_any()); + REQUIRE(v4_address == test_v6_any_addr_with_port); + wil::networking::socket_address v6_address; v6_address.set_address_any(AF_INET6); REQUIRE(v6_address.family() == AF_INET6); @@ -516,6 +523,13 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address.is_address_any()); REQUIRE(v6_address == test_v6_any_addr_with_port); + // verify changing families + v6_address.set_address_any(AF_INET); + REQUIRE(v6_address.family() == AF_INET); + REQUIRE(v6_address.port() == TestPort); // TODO: I'm seriously debating having set_address* clear the port + REQUIRE(v6_address.is_address_any()); + REQUIRE(v6_address == test_v4_any_addr_with_port); + wil::networking::socket_address defaulted_v4_address{AF_INET}; defaulted_v4_address.set_address_any(); REQUIRE(defaulted_v4_address.family() == AF_INET); From ce07fdf45d8688f47cd520dd67e664b72d1a5ed4 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Fri, 1 Nov 2024 00:12:21 -0700 Subject: [PATCH 08/22] more tests, more subtle fixes --- include/wil/networking.h | 189 ++++++++++++++++++++------------------ tests/NetworkingTests.cpp | 36 ++++++++ 2 files changed, 134 insertions(+), 91 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index 911b39fd..f82babcd 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -23,17 +23,22 @@ // define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* APIs #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) -#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - define WIN32_LEAN_AND_MEAN to avoid winsock.h included with windows.h +#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h #endif +//! Link libs: ws2_32.lib, ntdll.lib + +//! Adding related networking headers in this specific sequence +//! These headers have intra-header dependencies +//! This specific sequence should compile correctly and give access to #ifdef'd functions and types #include #include #include #include #include #include +#include -// wil headers #include "resource.h" namespace wil @@ -65,7 +70,7 @@ namespace networking return return_cleanup; } - //! Calls WSAStartup and fail-fasts if it fails; returns an RAII object that reverts + //! Calls WSAStartup and fail-fasts on error; returns an RAII object that reverts WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() { WSADATA unused_data{}; @@ -74,6 +79,7 @@ namespace networking } #ifdef WIL_ENABLE_EXCEPTIONS + //! Calls WSAStartup and throws on error; returns an RAII object that reverts WI_NODISCARD inline unique_wsacleanup_call WSAStartup() { WSADATA unused_data{}; @@ -218,6 +224,7 @@ namespace networking #endif // INET6_ADDRSTRLEN is guaranteed to be larger than INET_ADDRSTRLEN for IPv4 addresses + static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN); typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; @@ -233,7 +240,9 @@ namespace networking explicit socket_address(const SOCKET_ADDRESS*) WI_NOEXCEPT; explicit socket_address(const IN_ADDR*, unsigned short port = 0) WI_NOEXCEPT; explicit socket_address(const IN6_ADDR*, unsigned short port = 0) WI_NOEXCEPT; - +#ifdef WIL_ENABLE_EXCEPTIONS + explicit socket_address(const PCWSTR, unsigned short port = 0) WI_NOEXCEPT; +#endif ~socket_address() = default; socket_address(const socket_address&) WI_NOEXCEPT = default; socket_address& operator=(const socket_address&) WI_NOEXCEPT = default; @@ -268,7 +277,8 @@ namespace networking void set_scope_id(ULONG) WI_NOEXCEPT; void set_flow_info(ULONG) WI_NOEXCEPT; - // set_address* preserves the existing address family and port in the object + // set_address* preserves the existing port set on the address. + // the address family is preserved unless it is specified as an argument void set_address_any() WI_NOEXCEPT; void set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT; @@ -276,11 +286,11 @@ namespace networking void set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT; void set_address(const IN_ADDR*) WI_NOEXCEPT; void set_address(const IN6_ADDR*) WI_NOEXCEPT; + + // these set_address_nothrow functions do not preserve any existing fields like port [[nodiscard]] HRESULT set_address_nothrow(SOCKET) WI_NOEXCEPT; [[nodiscard]] HRESULT set_address_nothrow(_In_ PCWSTR) WI_NOEXCEPT; -#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS // ANSI functions are deprecated [[nodiscard]] HRESULT set_address_nothrow(_In_ PCSTR) WI_NOEXCEPT; -#endif // write_address prints the IP address portion, not the scope id or port #if defined(_STRING_) || defined(WIL_DOXYGEN) @@ -328,8 +338,8 @@ namespace networking SOCKADDR_INET m_sockaddr{}; }; - // for dual-mode sockets, when needing to explicitly connect to the target IPv4 address, - // - one must map the IPv4 address to its mapped IPv6 address + // When using dual-mode sockets, one might need to connect to a target IPv4 address. + // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address inline socket_address map_dual_mode_4to6(const socket_address& inV4) WI_NOEXCEPT { constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; @@ -397,6 +407,13 @@ namespace networking set_port(port); } +#ifdef WIL_ENABLE_EXCEPTIONS + inline socket_address::socket_address(const PCWSTR addr, unsigned short port) WI_NOEXCEPT + { + THROW_IF_FAILED(set_address_nothrow(addr)); + set_port(port); + } +#endif inline bool socket_address::operator==(const socket_address& rhs) const WI_NOEXCEPT { const auto& lhs = *this; @@ -423,7 +440,6 @@ namespace networking inline bool socket_address::operator<(const socket_address& rhs) const WI_NOEXCEPT { const auto& lhs = *this; - // Follows the same documented comparison logic as GetTcpTable2 and GetTcp6Table2 if (lhs.family() != rhs.family()) { return lhs.family() < rhs.family(); @@ -431,10 +447,34 @@ namespace networking if (lhs.family() == AF_INET) { - // don't compare the padding at the end of the SOCKADDR_IN - return ::memcmp(&lhs.m_sockaddr.Ipv4, &rhs.m_sockaddr.Ipv4, sizeof(SOCKADDR_IN) - sizeof(SOCKADDR_IN::sin_zero)) < 0; + // compare the address-only first + auto comparison = ::memcmp(lhs.in_addr(), rhs.in_addr(), sizeof(IN_ADDR)); + if (comparison != 0) + { + return comparison < 0; + } + // then compare the port (host-byte-order) + return lhs.port() < rhs.port(); + } + + // compare the address-only first + auto comparison = ::memcmp(lhs.in6_addr(), rhs.in6_addr(), sizeof(IN6_ADDR)); + if (comparison != 0) + { + return comparison < 0; + } + // then compare the scope_id of the address + if (lhs.scope_id() != rhs.scope_id()) + { + return lhs.scope_id() < rhs.scope_id(); } - return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) < 0; + // then compare flow_info + if (lhs.flow_info() != rhs.flow_info()) + { + return lhs.flow_info() < rhs.flow_info(); + } + // then compare the port (host-byte-order) + return lhs.port() < rhs.port(); } inline bool socket_address::operator>(const socket_address& rhs) const WI_NOEXCEPT @@ -485,7 +525,7 @@ namespace networking inline void socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { FAIL_FAST_IF_MSG( - addr->iSockaddrLength > c_sockaddr_size, + addr->lpSockaddr && addr->iSockaddrLength > c_sockaddr_size, "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", addr->iSockaddrLength); @@ -575,21 +615,11 @@ namespace networking inline void socket_address::set_port(USHORT port) WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET || family() == AF_INET6); + // the port value is at the exact same offset with both the IPv4 and IPv6 unions + static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); // port values in a sockaddr are always in network-byte order - USHORT port_network_byte_order = htons(port); - switch (family()) - { - case AF_INET: - m_sockaddr.Ipv4.sin_port = port_network_byte_order; - break; - - case AF_INET6: - m_sockaddr.Ipv6.sin6_port = port_network_byte_order; - break; - - default: - WI_ASSERT_MSG(false, "Unknown address family"); - } + m_sockaddr.Ipv4.sin_port = ::htons(port); } inline void socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT @@ -617,29 +647,12 @@ namespace networking inline void socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT { - switch (family) - { - case AF_INET: - { - auto original_port = m_sockaddr.Ipv4.sin_port; - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - m_sockaddr.Ipv4.sin_port = original_port; - break; - } - - case AF_INET6: - { - auto original_port = m_sockaddr.Ipv6.sin6_port; - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - m_sockaddr.Ipv6.sin6_port = original_port; - break; - } - - default: - FAIL_FAST_MSG("Unknown family (%d)", family); - } - - m_sockaddr.si_family = family; + // the port value is at the exact same offset with both the IPv4 and IPv6 unions + static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); + const auto original_port = m_sockaddr.Ipv4.sin_port; + WI_ASSERT(family == AF_INET || family == AF_INET6); + reset(family); + m_sockaddr.Ipv4.sin_port = original_port; } inline void socket_address::set_address_loopback() WI_NOEXCEPT @@ -649,53 +662,45 @@ namespace networking inline void socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT { + // the port value is at the exact same offset with both the IPv4 and IPv6 unions + static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); + const auto original_port = m_sockaddr.Ipv4.sin_port; + reset(family); switch (family) { case AF_INET: - { - auto original_port = m_sockaddr.Ipv4.sin_port; - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - m_sockaddr.Ipv4.sin_port = original_port; m_sockaddr.Ipv4.sin_addr.s_addr = INADDR_LOOPBACK; break; - } - case AF_INET6: - { - auto original_port = m_sockaddr.Ipv6.sin6_port; - // some compilers will not allow assignment of IN6ADDR_LOOPBACK_INIT - IN6_SET_ADDR_LOOPBACK(&m_sockaddr.Ipv6.sin6_addr); - m_sockaddr.Ipv6.sin6_port = original_port; + m_sockaddr.Ipv6.sin6_addr = {{IN6ADDR_LOOPBACK_INIT}}; break; - } - default: - FAIL_FAST_MSG("Unknown family (%d)", family); + WI_ASSERT_MSG(false, "Unknown address family"); } - - m_sockaddr.si_family = family; + m_sockaddr.Ipv4.sin_port = original_port; } inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT { - WI_ASSERT(family() == AF_INET); const auto original_port = m_sockaddr.Ipv4.sin_port; + WI_ASSERT(family() == AF_INET); reset(AF_INET); - m_sockaddr.Ipv4.sin_port = original_port; m_sockaddr.Ipv4.sin_addr.s_addr = addr->s_addr; + m_sockaddr.Ipv4.sin_port = original_port; } inline void socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT { + const auto original_port = m_sockaddr.Ipv6.sin6_port; WI_ASSERT(family() == AF_INET6); - const auto original_port = m_sockaddr.Ipv4.sin_port; reset(AF_INET6); - m_sockaddr.Ipv6.sin6_port = original_port; m_sockaddr.Ipv6.sin6_addr = *addr; + m_sockaddr.Ipv6.sin6_port = original_port; } inline HRESULT socket_address::set_address_nothrow(SOCKET s) WI_NOEXCEPT { + reset(AF_UNSPEC); auto nameLength = length(); auto error = ::getsockname(s, sockaddr(), &nameLength); if (error != 0) @@ -708,37 +713,39 @@ namespace networking inline HRESULT socket_address::set_address_nothrow(_In_ PCWSTR wszAddr) WI_NOEXCEPT { - ADDRINFOW hints{}; - hints.ai_flags = AI_NUMERICHOST; - - ADDRINFOW* pResult{}; - const auto error = ::GetAddrInfoW(wszAddr, nullptr, &hints, &pResult); - if (0 == error) + reset(AF_UNSPEC); + PCWSTR terminator_unused; + constexpr BOOLEAN strict_string = TRUE; + if (RtlIpv4StringToAddressW(wszAddr, strict_string, &terminator_unused, in_addr()) == 0) { - set_sockaddr(pResult->ai_addr, pResult->ai_addrlen); - ::FreeAddrInfoW(pResult); + m_sockaddr.si_family = AF_INET; return S_OK; } - RETURN_WIN32(error); + if (RtlIpv6StringToAddressW(wszAddr, &terminator_unused, in6_addr()) == 0) + { + m_sockaddr.si_family = AF_INET6; + return S_OK; + } + return E_INVALIDARG; } -// the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API -#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS inline HRESULT socket_address::set_address_nothrow(_In_ PCSTR szAddr) WI_NOEXCEPT { - ADDRINFOA hints{}; - hints.ai_flags = AI_NUMERICHOST; - - ADDRINFOA* pResult{}; - const auto error = ::GetAddrInfoA(szAddr, nullptr, &hints, &pResult) if (0 == error) + reset(AF_UNSPEC); + PCSTR terminator_unused; + constexpr BOOLEAN strict_string = TRUE; + if (RtlIpv4StringToAddressA(szAddr, strict_string, &terminator_unused, in_addr()) == 0) { - set_sockaddr(pResult->ai_addr, pResult->ai_addrlen); - FreeAddrInfoA(pResult); + m_sockaddr.si_family = AF_INET; return S_OK; } - RETURN_WIN32(error); + if (RtlIpv6StringToAddressA(szAddr, &terminator_unused, in6_addr()) == 0) + { + m_sockaddr.si_family = AF_INET6; + return S_OK; + } + return E_INVALIDARG; } -#endif #if defined(_STRING_) || defined(WIL_DOXYGEN) inline std::wstring socket_address::write_address() const diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index b4b8f190..3cd2e64f 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -455,6 +455,42 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") } } +TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") +{ +#ifdef WIL_ENABLE_EXCEPTIONS + using wil::networking::socket_address; + REQUIRE(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.1"})); + REQUIRE(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.1"})); + + REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.2"}); + REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.2"})); + REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.2"}); + REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.2"})); + + REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"2.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"2.1.1.1"})); + REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"2.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"2.1.1.1"})); + + REQUIRE(socket_address{L"1.1.1.1"} > socket_address{L"0.0.0.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} < socket_address{L"0.0.0.1"})); + REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"0.0.0.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"0.0.0.1"})); + + REQUIRE(socket_address{L"1.1.1.1", 1} < socket_address{L"1.1.1.1", 2}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} > socket_address{L"1.1.1.1", 2})); + REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"1.1.1.1", 2}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"1.1.1.1", 2})); + + REQUIRE(socket_address{L"1.1.1.1", 1} > socket_address{L"0.0.0.0", 65535}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} < socket_address{L"0.0.0.0", 65535})); + REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"0.0.0.0", 65535}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"0.0.0.0", 65535})); +#endif +} + TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") { InitTestAddresses(); From 9dc0852f1fd215f7d58f68f773c42c3adb6e0259 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Mon, 18 Nov 2024 00:36:29 -0800 Subject: [PATCH 09/22] Many more updates --- include/wil/networking.h | 312 ++++++++++++++++++++++++++++++++------ tests/NetworkingTests.cpp | 54 +++---- 2 files changed, 290 insertions(+), 76 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index f82babcd..4057ddd7 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -9,7 +9,7 @@ // //********************************************************* //! @file -//! Helpers for using BSD sockets and Windows Winsock APIs and structures. +//! Helpers for using BSD sockets and Winsock functions and structures. //! Does not require the use of the STL or C++ exceptions (see _nothrow functions) #ifndef __WIL_NETWORKING_INCLUDED #define __WIL_NETWORKING_INCLUDED @@ -18,26 +18,33 @@ #error This header is not supported in kernel-mode. #endif -// including winsock2.h before windows.h, to prevent inclusion of the older Winsock 1.1. header winsock.h in windows.h -// alternatively, one can define WIN32_LEAN_AND_MEAN to avoid windows.h including any sockets headers -// define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* APIs +//! define WIN32_LEAN_AND_MEAN at the project level to avoid windows.h including older winsock 1.1 headers from winsock.h +//! as including both the Winsock 1.1 header winsock.h and the Winsock 2 header winsock2.h will create compiler errors +//! alternatively, including winsock2.h before windows.h to prevent inclusion of the Winsock 1.1 winsock.h header from within windows.h +//! note, winsock2.h will include windows.h if not already included +//! +//! define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* functions in ws2tcpip.h +//! +//! define INCL_WINSOCK_API_TYPEDEFS at the project level to make function typedefs available across various networking headers +//! note, winsock2.h defaults is to not include function typedefs - but these can be necessary when supporting multiple OS versions +//! +//! Link libs for functions referenced in this file: ws2_32.lib, ntdll.lib, and Fwpuclnt.lib (for secure socket functions) #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) #error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h #endif -//! Link libs: ws2_32.lib, ntdll.lib - //! Adding related networking headers in this specific sequence -//! These headers have intra-header dependencies -//! This specific sequence should compile correctly and give access to #ifdef'd functions and types +//! These headers have many inter-header dependencies +//! This specific sequence should compile correctly and give access to all available #ifdef'd functions and types #include #include #include +#include #include #include +#include #include -#include #include "resource.h" @@ -53,7 +60,7 @@ namespace networking using unique_wsacleanup_call = unique_call; //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed - WI_NODISCARD inline unique_wsacleanup_call WSAStartup_nothrow() + WI_NODISCARD inline unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT { WSADATA unused_data{}; const auto error = ::WSAStartup(WINSOCK_VERSION, &unused_data); @@ -71,7 +78,7 @@ namespace networking } //! Calls WSAStartup and fail-fasts on error; returns an RAII object that reverts - WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() + WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() WI_NOEXCEPT { WSADATA unused_data{}; FAIL_FAST_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); @@ -88,6 +95,241 @@ namespace networking } #endif + struct WINSOCK_EXTENSION_FUNCTION_TABLE + { + LPFN_ACCEPTEX AcceptEx{nullptr}; + LPFN_CONNECTEX ConnectEx{nullptr}; + LPFN_DISCONNECTEX DisconnectEx{nullptr}; + LPFN_GETACCEPTEXSOCKADDRS GetAcceptExSockaddrs{nullptr}; + LPFN_TRANSMITFILE TransmitFile{nullptr}; + LPFN_TRANSMITPACKETS TransmitPackets{nullptr}; + LPFN_WSARECVMSG WSARecvMsg{nullptr}; + LPFN_WSASENDMSG WSASendMsg{nullptr}; + }; + struct winsock_extension_function_table + { + static winsock_extension_function_table load() WI_NOEXCEPT; + + ~winsock_extension_function_table() WI_NOEXCEPT = default; + + // can copy, but the new object needs its own WSA refcount + winsock_extension_function_table(const winsock_extension_function_table& rhs) WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + { + if (!wsa_refcount || !rhs.wsa_refcount) + { + ::ZeroMemory(&f, sizeof(f)); + successfully_loaded = false; + } + else + { + ::memcpy(&f, &rhs.f, sizeof(f)); + successfully_loaded = rhs.successfully_loaded; + } + } + winsock_extension_function_table& operator=(const winsock_extension_function_table& rhs) WI_NOEXCEPT + { + if (!wsa_refcount || !rhs.wsa_refcount) + { + ::ZeroMemory(&f, sizeof(f)); + successfully_loaded = false; + } + else + { + ::memcpy(&f, &rhs.f, sizeof(f)); + successfully_loaded = rhs.successfully_loaded; + } + + return *this; + } + + WINSOCK_EXTENSION_FUNCTION_TABLE f{}; + bool successfully_loaded{}; + + private: + // constructed via load() + winsock_extension_function_table() WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + { + successfully_loaded = static_cast(wsa_refcount); + } + + // must guarantee Winsock does not unload while we have dynamically loaded function pointers + const unique_wsacleanup_call wsa_refcount; + }; + + struct rio_extension_function_table + { + static rio_extension_function_table load() WI_NOEXCEPT; + + ~rio_extension_function_table() WI_NOEXCEPT = default; + + // can copy, but the new object needs its own WSA refcount + rio_extension_function_table(const rio_extension_function_table& rhs) WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + { + if (!wsa_refcount || !rhs.wsa_refcount) + { + // the only time we must make wsa_refcount not actually const + const_cast(&wsa_refcount)->release(); + successfully_loaded = false; + } + else + { + ::memcpy(&f, &rhs.f, sizeof(f)); + successfully_loaded = rhs.successfully_loaded; + } + } + rio_extension_function_table& operator=(const rio_extension_function_table& rhs) WI_NOEXCEPT + { + if (!wsa_refcount || !rhs.wsa_refcount) + { + // the only time we must make wsa_refcount not actually const + const_cast(&wsa_refcount)->release(); + successfully_loaded = false; + } + else + { + ::memcpy(&f, &rhs.f, sizeof(f)); + successfully_loaded = rhs.successfully_loaded; + } + + return *this; + } + + RIO_EXTENSION_FUNCTION_TABLE f{}; + bool successfully_loaded{false}; + + private: + // constructed via load() + rio_extension_function_table() WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + { + successfully_loaded = static_cast(wsa_refcount); + } + // must guarantee Winsock does not unload while we have dynamically loaded function pointers + const unique_wsacleanup_call wsa_refcount; + }; + + inline winsock_extension_function_table winsock_extension_function_table::load() WI_NOEXCEPT + { + winsock_extension_function_table table{}; + if (!table.successfully_loaded) + { + return table; + } + + // we need a temp socket to load the function table + const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + table.successfully_loaded = false; + return table; + } + + table.successfully_loaded = true; + + // walk WINSOCK_EXTENSION_FUNCTION_TABLE to load each function pointer + constexpr auto function_table_length = sizeof(table.f) / sizeof(void*); + for (auto fnLoop = 0ul; fnLoop < function_table_length; ++fnLoop) + { + void* functionPtr{}; + const GUID* extensionGuid{}; + + switch (fnLoop) + { + case 0: + { + functionPtr = &table.f.AcceptEx; + constexpr GUID acceptex_guid = WSAID_ACCEPTEX; + extensionGuid = &acceptex_guid; + break; + } + case 1: + { + functionPtr = &table.f.ConnectEx; + constexpr GUID connectex_guid = WSAID_CONNECTEX; + extensionGuid = &connectex_guid; + break; + } + case 2: + { + functionPtr = &table.f.DisconnectEx; + constexpr GUID disconnectex_guid = WSAID_DISCONNECTEX; + extensionGuid = &disconnectex_guid; + break; + } + case 3: + { + functionPtr = &table.f.GetAcceptExSockaddrs; + constexpr GUID getacceptexsockaddrs_guid = WSAID_GETACCEPTEXSOCKADDRS; + extensionGuid = &getacceptexsockaddrs_guid; + break; + } + case 4: + { + functionPtr = &table.f.TransmitFile; + constexpr GUID transmitfile_guid = WSAID_TRANSMITFILE; + extensionGuid = &transmitfile_guid; + break; + } + case 5: + { + functionPtr = &table.f.TransmitPackets; + constexpr GUID transmitpackets_guid = WSAID_TRANSMITPACKETS; + extensionGuid = &transmitpackets_guid; + break; + } + case 6: + { + functionPtr = &table.f.WSARecvMsg; + constexpr GUID wsarecvmsg_guid = WSAID_WSARECVMSG; + extensionGuid = &wsarecvmsg_guid; + break; + } + case 7: + { + functionPtr = &table.f.WSASendMsg; + constexpr GUID wsasendmsg_guid = WSAID_WSASENDMSG; + extensionGuid = &wsasendmsg_guid; + break; + } + } + + constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(void*)}; + DWORD unused_bytes; + if (0 != + ::WSAIoctl(localSocket.get(), controlCode, &extensionGuid, DWORD{sizeof(GUID)}, functionPtr, bytes, &unused_bytes, nullptr, nullptr)) + { + table.successfully_loaded = false; + } + } + + return table; + } + + rio_extension_function_table rio_extension_function_table::load() WI_NOEXCEPT + { + rio_extension_function_table table{}; + if (table.successfully_loaded) + { + // we need a temp socket to load the function table + const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (localSocket) + { + constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(table.f)}; + + GUID rioGuid = WSAID_MULTIPLE_RIO; + ::ZeroMemory(&table.f, bytes); + table.f.cbSize = bytes; + DWORD unused_bytes; + if (0 == ::WSAIoctl(localSocket.get(), controlCode, &rioGuid, DWORD{sizeof(rioGuid)}, &table.f, bytes, &unused_bytes, nullptr, nullptr)) + { + table.successfully_loaded = true; + } + } + } + return table; + } + // // encapsulates working with the sockaddr datatype // // sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) @@ -124,8 +366,9 @@ namespace networking // - not a derived sockaddr* type // - a structure containing both a sockaddr* and its length fields // + // TODO: what to do with this oddball // SOCKADDR_DL... data-link - + // class addr_info; addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; @@ -223,7 +466,6 @@ namespace networking #endif - // INET6_ADDRSTRLEN is guaranteed to be larger than INET_ADDRSTRLEN for IPv4 addresses static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN); typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; @@ -243,6 +485,7 @@ namespace networking #ifdef WIL_ENABLE_EXCEPTIONS explicit socket_address(const PCWSTR, unsigned short port = 0) WI_NOEXCEPT; #endif + ~socket_address() = default; socket_address(const socket_address&) WI_NOEXCEPT = default; socket_address& operator=(const socket_address&) WI_NOEXCEPT = default; @@ -258,7 +501,6 @@ namespace networking void reset(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; // set_sockaddr overwrites the entire sockaddr in the object - template void set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT; void set_sockaddr(const SOCKADDR_IN*) WI_NOEXCEPT; @@ -266,20 +508,19 @@ namespace networking void set_sockaddr(const SOCKADDR_INET*) WI_NOEXCEPT; void set_sockaddr(const SOCKET_ADDRESS*) WI_NOEXCEPT; - [[nodiscard]] bool is_address_any() const WI_NOEXCEPT; [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; [[nodiscard]] bool is_address_loopback() const WI_NOEXCEPT; - // returns NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, + // returns NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, NlatInvalid [[nodiscard]] NL_ADDRESS_TYPE get_address_type() const WI_NOEXCEPT; void set_port(USHORT) WI_NOEXCEPT; void set_scope_id(ULONG) WI_NOEXCEPT; void set_flow_info(ULONG) WI_NOEXCEPT; - // set_address* preserves the existing port set on the address. - // the address family is preserved unless it is specified as an argument - + // set_address* preserves the existing port set on the address + // - so that one can call set_port() and set_address() in any order with expected results + // - the address family is preserved unless it is specified (or inferred) as an argument void set_address_any() WI_NOEXCEPT; void set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT; void set_address_loopback() WI_NOEXCEPT; @@ -295,18 +536,14 @@ namespace networking // write_address prints the IP address portion, not the scope id or port #if defined(_STRING_) || defined(WIL_DOXYGEN) [[nodiscard]] std::wstring write_address() const; + [[nodiscard]] std::wstring write_complete_address() const; #endif HRESULT write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; - // write_complete_address prints the IP address, scope id, and port -#if defined(_STRING_) || defined(WIL_DOXYGEN) - [[nodiscard]] std::wstring write_complete_address() const; -#endif + // write_complete_address_nothrow() prints the IP address as well as the scope id and port values HRESULT write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; -#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS // ANSI functions are deprecated HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; -#endif // Accessors [[nodiscard]] ADDRESS_FAMILY family() const WI_NOEXCEPT; @@ -536,29 +773,6 @@ namespace networking } } - inline bool socket_address::is_address_any() const WI_NOEXCEPT - { - if (scope_id() != 0) - { - return false; - } - - switch (family()) - { - case AF_UNSPEC: - return false; - - case AF_INET: - return ::IN4_IS_ADDR_UNSPECIFIED(in_addr()); - - case AF_INET6: - return ::IN6_IS_ADDR_UNSPECIFIED(in6_addr()); - } - - WI_ASSERT_MSG(false, "Unknown address family"); - return false; - } - inline bool socket_address::is_address_linklocal() const WI_NOEXCEPT { switch (family()) @@ -936,4 +1150,4 @@ namespace networking } // namespace networking } // namespace wil -#endif +#endif // __WIL_NETWORKING_INCLUDED diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 3cd2e64f..c1e674bb 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -151,7 +151,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("Default socket address properties") { REQUIRE(default_addr.family() == AF_UNSPEC); - REQUIRE(!default_addr.is_address_any()); + REQUIRE(default_addr.get_address_type() == NlatUnspecified); REQUIRE(!default_addr.is_address_linklocal()); REQUIRE(!default_addr.is_address_loopback()); REQUIRE(NlatUnspecified == default_addr.get_address_type()); @@ -162,7 +162,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 in_addr properties") { REQUIRE(test_v4_addr.family() == AF_INET); - REQUIRE(!test_v4_addr.is_address_any()); + REQUIRE(test_v4_addr.get_address_type() == NlatUnicast); REQUIRE(!test_v4_addr.is_address_linklocal()); REQUIRE(!test_v4_addr.is_address_loopback()); REQUIRE(NlatUnicast == test_v4_addr.get_address_type()); @@ -185,7 +185,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 in_addr with port properties") { REQUIRE(test_v4_addr_with_port.family() == AF_INET); - REQUIRE(!test_v4_addr_with_port.is_address_any()); + REQUIRE(test_v4_addr_with_port.get_address_type() == NlatUnicast); REQUIRE(!test_v4_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_addr_with_port.is_address_loopback()); REQUIRE(NlatUnicast == test_v4_addr_with_port.get_address_type()); @@ -200,14 +200,14 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_addr_with_port != default_addr); REQUIRE(test_v4_addr_with_port != test_v4_addr); REQUIRE(test_v4_addr_with_port > test_v4_addr); - REQUIRE(test_v4_addr_with_port > test_v4_addr2); + REQUIRE(test_v4_addr_with_port < test_v4_addr2); REQUIRE(test_v4_addr_with_port > default_addr); } SECTION("IPv6 in6_addr properties") { REQUIRE(test_v6_addr.family() == AF_INET6); - REQUIRE(!test_v6_addr.is_address_any()); + REQUIRE(test_v6_addr.get_address_type() == NlatUnicast); REQUIRE(!test_v6_addr.is_address_linklocal()); REQUIRE(!test_v6_addr.is_address_loopback()); REQUIRE(NlatUnicast == test_v6_addr.get_address_type()); @@ -232,7 +232,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 in6_addr with port properties") { REQUIRE(test_v6_addr_with_port.family() == AF_INET6); - REQUIRE(!test_v6_addr_with_port.is_address_any()); + REQUIRE(test_v6_addr_with_port.get_address_type() == NlatUnicast); REQUIRE(!test_v6_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_addr_with_port.is_address_loopback()); REQUIRE(NlatUnicast == test_v6_addr_with_port.get_address_type()); @@ -249,7 +249,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_addr_with_port != test_v6_addr); REQUIRE(test_v6_addr_with_port != test_v6_addr2); REQUIRE(test_v6_addr_with_port > test_v6_addr); - REQUIRE(test_v6_addr_with_port > test_v6_addr2); + REQUIRE(test_v6_addr_with_port < test_v6_addr2); REQUIRE(test_v6_addr_with_port != default_addr); REQUIRE(test_v6_addr_with_port > default_addr); } @@ -257,7 +257,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 link-local in_addr properties") { REQUIRE(test_v4_linklocal_addr.family() == AF_INET); - REQUIRE(!test_v4_linklocal_addr.is_address_any()); + REQUIRE(test_v4_linklocal_addr.get_address_type() == NlatUnicast); REQUIRE(test_v4_linklocal_addr.is_address_linklocal()); REQUIRE(!test_v4_linklocal_addr.is_address_loopback()); REQUIRE(NlatUnicast == test_v4_linklocal_addr.get_address_type()); @@ -278,7 +278,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 link-local in_addr with port properties") { REQUIRE(test_v4_linklocal_addr_with_port.family() == AF_INET); - REQUIRE(!test_v4_linklocal_addr_with_port.is_address_any()); + REQUIRE(test_v4_linklocal_addr_with_port.get_address_type() == NlatUnicast); REQUIRE(test_v4_linklocal_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_linklocal_addr_with_port.is_address_loopback()); REQUIRE(NlatUnicast == test_v4_linklocal_addr_with_port.get_address_type()); @@ -301,7 +301,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 link-local in6_addr properties") { REQUIRE(test_v6_linklocal_addr.family() == AF_INET6); - REQUIRE(!test_v6_linklocal_addr.is_address_any()); + REQUIRE(test_v6_linklocal_addr.get_address_type() == NlatUnicast); REQUIRE(test_v6_linklocal_addr.is_address_linklocal()); REQUIRE(!test_v6_linklocal_addr.is_address_loopback()); REQUIRE(NlatUnicast == test_v6_linklocal_addr.get_address_type()); @@ -324,7 +324,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 link-local in6_addr with port properties") { REQUIRE(test_v6_linklocal_addr_with_port.family() == AF_INET6); - REQUIRE(!test_v6_linklocal_addr_with_port.is_address_any()); + REQUIRE(test_v6_linklocal_addr_with_port.get_address_type() == NlatUnicast); REQUIRE(test_v6_linklocal_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_linklocal_addr_with_port.is_address_loopback()); REQUIRE(NlatUnicast == test_v6_linklocal_addr_with_port.get_address_type()); @@ -349,7 +349,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 any-addr in_addr properties") { REQUIRE(test_v4_any_addr.family() == AF_INET); - REQUIRE(test_v4_any_addr.is_address_any()); + REQUIRE(test_v4_any_addr.get_address_type() == NlatUnspecified); REQUIRE(!test_v4_any_addr.is_address_linklocal()); REQUIRE(!test_v4_any_addr.is_address_loopback()); REQUIRE(NlatUnspecified == test_v4_any_addr.get_address_type()); @@ -374,7 +374,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 any-addr in_addr with port properties") { REQUIRE(test_v4_any_addr_with_port.family() == AF_INET); - REQUIRE(test_v4_any_addr_with_port.is_address_any()); + REQUIRE(test_v4_any_addr_with_port.get_address_type() == NlatUnspecified); REQUIRE(!test_v4_any_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_any_addr_with_port.is_address_loopback()); REQUIRE(NlatUnspecified == test_v4_any_addr_with_port.get_address_type()); @@ -401,7 +401,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 any-addr in6_addr properties") { REQUIRE(test_v6_any_addr.family() == AF_INET6); - REQUIRE(test_v6_any_addr.is_address_any()); + REQUIRE(test_v6_any_addr.get_address_type() == NlatUnspecified); REQUIRE(!test_v6_any_addr.is_address_linklocal()); REQUIRE(!test_v6_any_addr.is_address_loopback()); REQUIRE(NlatUnspecified == test_v6_any_addr.get_address_type()); @@ -428,7 +428,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 any-addr in6_addr with port properties") { REQUIRE(test_v6_any_addr_with_port.family() == AF_INET6); - REQUIRE(test_v6_any_addr_with_port.is_address_any()); + REQUIRE(test_v6_any_addr_with_port.get_address_type() == NlatUnspecified); REQUIRE(!test_v6_any_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_any_addr_with_port.is_address_loopback()); REQUIRE(NlatUnspecified == test_v6_any_addr_with_port.get_address_type()); @@ -524,7 +524,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v4_address.set_address_any(AF_INET); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == 0); - REQUIRE(v4_address.is_address_any()); + REQUIRE(v4_address.get_address_type() == NlatUnspecified); REQUIRE(!v4_address.is_address_linklocal()); REQUIRE(!v4_address.is_address_loopback()); REQUIRE(NlatUnspecified == v4_address.get_address_type()); @@ -533,21 +533,21 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v4_address.set_port(TestPort); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == TestPort); - REQUIRE(v4_address.is_address_any()); + REQUIRE(v4_address.get_address_type() == NlatUnspecified); REQUIRE(v4_address == test_v4_any_addr_with_port); // verify changing families v4_address.set_address_any(AF_INET6); REQUIRE(v4_address.family() == AF_INET6); - REQUIRE(v4_address.port() == TestPort); // TODO: I'm seriously debating having set_address* clear the port - REQUIRE(v4_address.is_address_any()); + REQUIRE(v4_address.port() == TestPort); + REQUIRE(v4_address.get_address_type() == NlatUnspecified); REQUIRE(v4_address == test_v6_any_addr_with_port); wil::networking::socket_address v6_address; v6_address.set_address_any(AF_INET6); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == 0); - REQUIRE(v6_address.is_address_any()); + REQUIRE(v6_address.get_address_type() == NlatUnspecified); REQUIRE(!v6_address.is_address_linklocal()); REQUIRE(!v6_address.is_address_loopback()); REQUIRE(NlatUnspecified == v6_address.get_address_type()); @@ -556,21 +556,21 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v6_address.set_port(TestPort); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == TestPort); - REQUIRE(v6_address.is_address_any()); + REQUIRE(v6_address.get_address_type() == NlatUnspecified); REQUIRE(v6_address == test_v6_any_addr_with_port); // verify changing families v6_address.set_address_any(AF_INET); REQUIRE(v6_address.family() == AF_INET); - REQUIRE(v6_address.port() == TestPort); // TODO: I'm seriously debating having set_address* clear the port - REQUIRE(v6_address.is_address_any()); + REQUIRE(v6_address.port() == TestPort); + REQUIRE(v6_address.get_address_type() == NlatUnspecified); REQUIRE(v6_address == test_v4_any_addr_with_port); wil::networking::socket_address defaulted_v4_address{AF_INET}; defaulted_v4_address.set_address_any(); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == 0); - REQUIRE(defaulted_v4_address.is_address_any()); + REQUIRE(defaulted_v4_address.get_address_type() == NlatUnspecified); REQUIRE(!defaulted_v4_address.is_address_linklocal()); REQUIRE(!defaulted_v4_address.is_address_loopback()); REQUIRE(NlatUnspecified == defaulted_v4_address.get_address_type()); @@ -579,14 +579,14 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") defaulted_v4_address.set_port(TestPort); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == TestPort); - REQUIRE(defaulted_v4_address.is_address_any()); + REQUIRE(defaulted_v4_address.get_address_type() == NlatUnspecified); REQUIRE(defaulted_v4_address == test_v4_any_addr_with_port); wil::networking::socket_address defaulted_v6_address; defaulted_v6_address.set_address_any(AF_INET6); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == 0); - REQUIRE(defaulted_v6_address.is_address_any()); + REQUIRE(defaulted_v6_address.get_address_type() == NlatUnspecified); REQUIRE(!defaulted_v6_address.is_address_linklocal()); REQUIRE(!defaulted_v6_address.is_address_loopback()); REQUIRE(NlatUnspecified == defaulted_v6_address.get_address_type()); @@ -595,7 +595,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") defaulted_v6_address.set_port(TestPort); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == TestPort); - REQUIRE(defaulted_v6_address.is_address_any()); + REQUIRE(defaulted_v6_address.get_address_type() == NlatUnspecified); REQUIRE(defaulted_v6_address == test_v6_any_addr_with_port); } } \ No newline at end of file From 6c8644e510f86f6a33bc1866a5740ed34922555a Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Tue, 31 Dec 2024 16:45:40 -0800 Subject: [PATCH 10/22] Finished all unit tests --- include/wil/networking.h | 1094 +++++++++++++-------- tests/NetworkingTests.cpp | 1929 ++++++++++++++++++++++++++++++++++--- 2 files changed, 2515 insertions(+), 508 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index 4057ddd7..9b9425c4 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -19,9 +19,9 @@ #endif //! define WIN32_LEAN_AND_MEAN at the project level to avoid windows.h including older winsock 1.1 headers from winsock.h -//! as including both the Winsock 1.1 header winsock.h and the Winsock 2 header winsock2.h will create compiler errors +//! as including both the Winsock 1.1 header 'winsock.h' and the Winsock 2 header 'winsock2.h' will create compiler errors //! alternatively, including winsock2.h before windows.h to prevent inclusion of the Winsock 1.1 winsock.h header from within windows.h -//! note, winsock2.h will include windows.h if not already included +//! note: winsock2.h will include windows.h if not already included //! //! define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* functions in ws2tcpip.h //! @@ -34,9 +34,9 @@ #error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h #endif -//! Adding related networking headers in this specific sequence -//! These headers have many inter-header dependencies -//! This specific sequence should compile correctly and give access to all available #ifdef'd functions and types +//! Including Winsock and networking headers in the below specific sequence +//! These headers have many intra-header dependencies, creating difficulties when needing access to various functions and types +//! This specific sequence should compile correctly to give access to all available functions and types #include #include #include @@ -46,6 +46,7 @@ #include #include +// the wil header for RAII types #include "resource.h" namespace wil @@ -53,11 +54,12 @@ namespace wil //! Functions and classes that support networking operations and structures namespace networking { + class socket_address; //! A type that calls WSACleanup on destruction (or reset()). //! WSAStartup must be called for the lifetime of all Winsock APIs (synchronous and asynchronous) //! WSACleanup will unload the full Winsock catalog - all the libraries - with the final reference //! which can lead to crashes if socket APIs are still being used after the final WSACleanup is called - using unique_wsacleanup_call = unique_call; + using unique_wsacleanup_call = ::wil::unique_call; //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed WI_NODISCARD inline unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT @@ -71,7 +73,7 @@ namespace networking { // internally set m_call to false // so the caller can check the return object against its operator bool - // to determine if it succeeded + // to determine if WSAStartup succeeded return_cleanup.release(); } return return_cleanup; @@ -85,7 +87,7 @@ namespace networking return {}; } -#ifdef WIL_ENABLE_EXCEPTIONS +#if defined(WIL_ENABLE_EXCEPTIONS) //! Calls WSAStartup and throws on error; returns an RAII object that reverts WI_NODISCARD inline unique_wsacleanup_call WSAStartup() { @@ -106,54 +108,65 @@ namespace networking LPFN_WSARECVMSG WSARecvMsg{nullptr}; LPFN_WSASENDMSG WSASendMsg{nullptr}; }; + struct winsock_extension_function_table { static winsock_extension_function_table load() WI_NOEXCEPT; ~winsock_extension_function_table() WI_NOEXCEPT = default; - // can copy, but the new object needs its own WSA refcount - winsock_extension_function_table(const winsock_extension_function_table& rhs) WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + // can copy, but the new object needs its own WSA reference count + // (getting a WSA reference count should be no-fail once the first reference it taken) + // + // IF the target could not get a WSA reference count + // OR + // IF we couldn't get our own WSA reference count + // (this should never happen the caller has a reference, but we failed to get a WSA reference) + // THEN this object cannot carry forward any function pointers - it must show successfully_loaded == false + winsock_extension_function_table(const winsock_extension_function_table& rhs) WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} { - if (!wsa_refcount || !rhs.wsa_refcount) + if (!wsa_reference_count || !rhs.wsa_reference_count) { - ::ZeroMemory(&f, sizeof(f)); - successfully_loaded = false; + ::memset(&f, 0, sizeof(f)); } else { - ::memcpy(&f, &rhs.f, sizeof(f)); - successfully_loaded = rhs.successfully_loaded; + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); } } + winsock_extension_function_table& operator=(const winsock_extension_function_table& rhs) WI_NOEXCEPT { - if (!wsa_refcount || !rhs.wsa_refcount) + if (!wsa_reference_count || !rhs.wsa_reference_count) { - ::ZeroMemory(&f, sizeof(f)); - successfully_loaded = false; + ::memset(&f, 0, sizeof(f)); } else { - ::memcpy(&f, &rhs.f, sizeof(f)); - successfully_loaded = rhs.successfully_loaded; + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); } return *this; } + //! Returns true if all functions were loaded, holding a WSAStartup reference + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return f.AcceptEx != nullptr; + } + WINSOCK_EXTENSION_FUNCTION_TABLE f{}; - bool successfully_loaded{}; private: // constructed via load() - winsock_extension_function_table() WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + winsock_extension_function_table() WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} { - successfully_loaded = static_cast(wsa_refcount); } // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const unique_wsacleanup_call wsa_refcount; + const unique_wsacleanup_call wsa_reference_count; }; struct rio_extension_function_table @@ -162,173 +175,60 @@ namespace networking ~rio_extension_function_table() WI_NOEXCEPT = default; - // can copy, but the new object needs its own WSA refcount - rio_extension_function_table(const rio_extension_function_table& rhs) WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + // can copy, but the new object needs its own WSA reference count + // (getting a WSA reference count should be no-fail once the first reference it taken) + // + // IF the target could not get a WSA reference count + // OR + // IF we couldn't get our own WSA reference count + // (this should never happen the caller has a reference, but we failed to get a WSA reference) + // THEN this object cannot carry forward any function pointers - it must show successfully_loaded == false + rio_extension_function_table(const rio_extension_function_table& rhs) WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} { - if (!wsa_refcount || !rhs.wsa_refcount) + if (!wsa_reference_count || !rhs.wsa_reference_count) { - // the only time we must make wsa_refcount not actually const - const_cast(&wsa_refcount)->release(); - successfully_loaded = false; + ::memset(&f, 0, sizeof(f)); } else { - ::memcpy(&f, &rhs.f, sizeof(f)); - successfully_loaded = rhs.successfully_loaded; + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); } } + rio_extension_function_table& operator=(const rio_extension_function_table& rhs) WI_NOEXCEPT { - if (!wsa_refcount || !rhs.wsa_refcount) + if (!wsa_reference_count || !rhs.wsa_reference_count) { - // the only time we must make wsa_refcount not actually const - const_cast(&wsa_refcount)->release(); - successfully_loaded = false; + ::memset(&f, 0, sizeof(f)); } else { - ::memcpy(&f, &rhs.f, sizeof(f)); - successfully_loaded = rhs.successfully_loaded; + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); } return *this; } + //! Returns true if all functions were loaded, holding a WSAStartup reference + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return f.RIOReceive != nullptr; + } + RIO_EXTENSION_FUNCTION_TABLE f{}; - bool successfully_loaded{false}; private: // constructed via load() - rio_extension_function_table() WI_NOEXCEPT : wsa_refcount{WSAStartup_nothrow()} + rio_extension_function_table() WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} { - successfully_loaded = static_cast(wsa_refcount); } + // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const unique_wsacleanup_call wsa_refcount; + const unique_wsacleanup_call wsa_reference_count; }; - inline winsock_extension_function_table winsock_extension_function_table::load() WI_NOEXCEPT - { - winsock_extension_function_table table{}; - if (!table.successfully_loaded) - { - return table; - } - - // we need a temp socket to load the function table - const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (INVALID_SOCKET == localSocket.get()) - { - table.successfully_loaded = false; - return table; - } - - table.successfully_loaded = true; - - // walk WINSOCK_EXTENSION_FUNCTION_TABLE to load each function pointer - constexpr auto function_table_length = sizeof(table.f) / sizeof(void*); - for (auto fnLoop = 0ul; fnLoop < function_table_length; ++fnLoop) - { - void* functionPtr{}; - const GUID* extensionGuid{}; - - switch (fnLoop) - { - case 0: - { - functionPtr = &table.f.AcceptEx; - constexpr GUID acceptex_guid = WSAID_ACCEPTEX; - extensionGuid = &acceptex_guid; - break; - } - case 1: - { - functionPtr = &table.f.ConnectEx; - constexpr GUID connectex_guid = WSAID_CONNECTEX; - extensionGuid = &connectex_guid; - break; - } - case 2: - { - functionPtr = &table.f.DisconnectEx; - constexpr GUID disconnectex_guid = WSAID_DISCONNECTEX; - extensionGuid = &disconnectex_guid; - break; - } - case 3: - { - functionPtr = &table.f.GetAcceptExSockaddrs; - constexpr GUID getacceptexsockaddrs_guid = WSAID_GETACCEPTEXSOCKADDRS; - extensionGuid = &getacceptexsockaddrs_guid; - break; - } - case 4: - { - functionPtr = &table.f.TransmitFile; - constexpr GUID transmitfile_guid = WSAID_TRANSMITFILE; - extensionGuid = &transmitfile_guid; - break; - } - case 5: - { - functionPtr = &table.f.TransmitPackets; - constexpr GUID transmitpackets_guid = WSAID_TRANSMITPACKETS; - extensionGuid = &transmitpackets_guid; - break; - } - case 6: - { - functionPtr = &table.f.WSARecvMsg; - constexpr GUID wsarecvmsg_guid = WSAID_WSARECVMSG; - extensionGuid = &wsarecvmsg_guid; - break; - } - case 7: - { - functionPtr = &table.f.WSASendMsg; - constexpr GUID wsasendmsg_guid = WSAID_WSASENDMSG; - extensionGuid = &wsasendmsg_guid; - break; - } - } - - constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(void*)}; - DWORD unused_bytes; - if (0 != - ::WSAIoctl(localSocket.get(), controlCode, &extensionGuid, DWORD{sizeof(GUID)}, functionPtr, bytes, &unused_bytes, nullptr, nullptr)) - { - table.successfully_loaded = false; - } - } - - return table; - } - - rio_extension_function_table rio_extension_function_table::load() WI_NOEXCEPT - { - rio_extension_function_table table{}; - if (table.successfully_loaded) - { - // we need a temp socket to load the function table - const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (localSocket) - { - constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(table.f)}; - - GUID rioGuid = WSAID_MULTIPLE_RIO; - ::ZeroMemory(&table.f, bytes); - table.f.cbSize = bytes; - DWORD unused_bytes; - if (0 == ::WSAIoctl(localSocket.get(), controlCode, &rioGuid, DWORD{sizeof(rioGuid)}, &table.f, bytes, &unused_bytes, nullptr, nullptr)) - { - table.successfully_loaded = true; - } - } - } - return table; - } // // encapsulates working with the sockaddr datatype // @@ -349,123 +249,43 @@ namespace networking // // sockaddr_storage / SOCKADDR_STORAGE // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address - // // sockaddr_in / SOCKADDR_IN // - a sockaddr* derived type designed to contain an IPv4 address and port number // sockaddr_in6 / SOCKADDR_IN6 // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info // SOCKADDR_INET // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address - // - // in_addr (IN_ADDR) + // in_addr / IN_ADDR // - the raw address portion of a sockaddr_in - // in6_addr (IN6_ADDR) + // in6_addr / IN6_ADDR // - the raw address portion of a sockaddr_in6 // // SOCKET_ADDRESS // - not a derived sockaddr* type - // - a structure containing both a sockaddr* and its length fields - // - // TODO: what to do with this oddball - // SOCKADDR_DL... data-link + // - a structure containing both a sockaddr* and its length fields, returned from some networking functions // - class addr_info; - addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; - // class addr_info encapsulates the ADDRINFO structure - // this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo - // exposes iterator semantics to safely access these addresses - class addr_info + [[nodiscard]] inline bool equals(const in_addr& lhs, const in_addr& rhs) WI_NOEXCEPT { - public: - struct iterator - { - // TODO - }; - - iterator begin(); - iterator end(); - - [[nodiscard]] int get_last_error() const WI_NOEXCEPT - { - return m_lastError; - } - - addr_info(const addr_info&) = delete; - addr_info& operator=(const addr_info&) = delete; - - addr_info(addr_info&& rhs) WI_NOEXCEPT - { - m_addrResult = rhs.m_addrResult; - rhs.m_addrResult = nullptr; - } - - addr_info& operator=(addr_info&& rhs) WI_NOEXCEPT - { - if (m_addrResult) - { - ::FreeAddrInfoW(m_addrResult); - } - m_addrResult = rhs.m_addrResult; - rhs.m_addrResult = nullptr; - - return *this; - } - - ~addr_info() WI_NOEXCEPT - { - if (m_addrResult) - { - ::FreeAddrInfoW(m_addrResult); - } - } - - private: - friend addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT; -#ifdef WIL_ENABLE_EXCEPTIONS - friend addr_info resolve_name(_In_ PCWSTR name); -#endif - - addr_info(_In_ ADDRINFOW* addrResult, int error) : m_addrResult{addrResult}, m_lastError{error} - { - } - - ADDRINFOW* m_addrResult{}; - int m_lastError{}; - }; - - inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT - { - int lastError = 0; - ADDRINFOW* addrResult{}; - if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) - { - lastError = ::WSAGetLastError(); - } - - return {addrResult, lastError}; + return lhs.s_addr == rhs.s_addr; } - inline ::wil::networking::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT + [[nodiscard]] inline bool not_equals(const in_addr& lhs, const in_addr& rhs) WI_NOEXCEPT { - return ::wil::networking::resolve_name_nothrow(L""); + return lhs.s_addr != rhs.s_addr; } -#ifdef WIL_ENABLE_EXCEPTIONS - inline ::wil::networking::addr_info resolve_name(_In_ PCWSTR name) + [[nodiscard]] inline bool equals(const in6_addr& lhs, const in6_addr& rhs) WI_NOEXCEPT { - ADDRINFOW* addrResult{}; - THROW_IF_WIN32_ERROR(::GetAddrInfoW(name, nullptr, nullptr, &addrResult)); - return {addrResult, 0}; + return 0 == ::memcmp(&lhs, &rhs, sizeof(in6_addr)); } - inline ::wil::networking::addr_info resolve_local_addresses() WI_NOEXCEPT + [[nodiscard]] inline bool not_equals(const in6_addr& lhs, const in6_addr& rhs) WI_NOEXCEPT { - return ::wil::networking::resolve_name(L""); + return 0 != ::memcmp(&lhs, &rhs, sizeof(in6_addr)); } -#endif - + // declaring char-arrays large enough for any IPv4 or IPv6 address + optional fields static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN); typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; @@ -482,8 +302,8 @@ namespace networking explicit socket_address(const SOCKET_ADDRESS*) WI_NOEXCEPT; explicit socket_address(const IN_ADDR*, unsigned short port = 0) WI_NOEXCEPT; explicit socket_address(const IN6_ADDR*, unsigned short port = 0) WI_NOEXCEPT; -#ifdef WIL_ENABLE_EXCEPTIONS - explicit socket_address(const PCWSTR, unsigned short port = 0) WI_NOEXCEPT; +#if defined(WIL_ENABLE_EXCEPTIONS) + explicit socket_address(PCWSTR, unsigned short port = 0); #endif ~socket_address() = default; @@ -507,12 +327,14 @@ namespace networking void set_sockaddr(const SOCKADDR_IN6*) WI_NOEXCEPT; void set_sockaddr(const SOCKADDR_INET*) WI_NOEXCEPT; void set_sockaddr(const SOCKET_ADDRESS*) WI_NOEXCEPT; - - [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; - [[nodiscard]] bool is_address_loopback() const WI_NOEXCEPT; - - // returns NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, NlatInvalid - [[nodiscard]] NL_ADDRESS_TYPE get_address_type() const WI_NOEXCEPT; +#if defined(WIL_ENABLE_EXCEPTIONS) + void set_sockaddr(SOCKET); + void set_sockaddr(PCWSTR); + void set_sockaddr(PCSTR); +#endif + [[nodiscard]] HRESULT set_sockaddr_nothrow(SOCKET) WI_NOEXCEPT; + [[nodiscard]] HRESULT set_sockaddr_nothrow(PCWSTR) WI_NOEXCEPT; + [[nodiscard]] HRESULT set_sockaddr_nothrow(PCSTR) WI_NOEXCEPT; void set_port(USHORT) WI_NOEXCEPT; void set_scope_id(ULONG) WI_NOEXCEPT; @@ -528,13 +350,8 @@ namespace networking void set_address(const IN_ADDR*) WI_NOEXCEPT; void set_address(const IN6_ADDR*) WI_NOEXCEPT; - // these set_address_nothrow functions do not preserve any existing fields like port - [[nodiscard]] HRESULT set_address_nothrow(SOCKET) WI_NOEXCEPT; - [[nodiscard]] HRESULT set_address_nothrow(_In_ PCWSTR) WI_NOEXCEPT; - [[nodiscard]] HRESULT set_address_nothrow(_In_ PCSTR) WI_NOEXCEPT; - // write_address prints the IP address portion, not the scope id or port -#if defined(_STRING_) || defined(WIL_DOXYGEN) +#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) [[nodiscard]] std::wstring write_address() const; [[nodiscard]] std::wstring write_complete_address() const; #endif @@ -545,6 +362,11 @@ namespace networking HRESULT write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; + // type: NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, NlatInvalid + [[nodiscard]] NL_ADDRESS_TYPE address_type() const WI_NOEXCEPT; + [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; + [[nodiscard]] bool is_address_loopback() const WI_NOEXCEPT; + // Accessors [[nodiscard]] ADDRESS_FAMILY family() const WI_NOEXCEPT; [[nodiscard]] USHORT port() const WI_NOEXCEPT; @@ -565,43 +387,275 @@ namespace networking [[nodiscard]] const IN_ADDR* in_addr() const WI_NOEXCEPT; [[nodiscard]] const IN6_ADDR* in6_addr() const WI_NOEXCEPT; - [[nodiscard]] static int length() WI_NOEXCEPT + static constexpr int length{sizeof(SOCKADDR_INET)}; + + private: + SOCKADDR_INET m_sockaddr{}; + }; + + // When using dual-mode sockets, one might need to connect to a target IPv4 address. + // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address + inline socket_address map_dual_mode_4to6(const socket_address& inV4) WI_NOEXCEPT + { + constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; + + socket_address outV6{&v4MappedPrefix, inV4.port()}; + + auto* const pIn6Addr = outV6.in6_addr(); + const auto* const pIn4Addr = inV4.in_addr(); + pIn6Addr->u.Byte[12] = pIn4Addr->S_un.S_un_b.s_b1; + pIn6Addr->u.Byte[13] = pIn4Addr->S_un.S_un_b.s_b2; + pIn6Addr->u.Byte[14] = pIn4Addr->S_un.S_un_b.s_b3; + pIn6Addr->u.Byte[15] = pIn4Addr->S_un.S_un_b.s_b4; + + return outV6; + } + + // + // non-member swap + // + inline void swap(socket_address& lhs, socket_address& rhs) WI_NOEXCEPT + { + lhs.swap(rhs); + } + + //! class addr_info encapsulates the ADDRINFO structure + //! this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo + //! iterator semantics are supported to safely access these addresses + class addr_info + { + public: + addr_info(_In_ ADDRINFOW* addrResult, int error) WI_NOEXCEPT : + m_addrResult{addrResult}, + m_lastError{error} + { + } + + [[nodiscard]] int get_last_error() const WI_NOEXCEPT + { + return m_lastError; + } + + ~addr_info() WI_NOEXCEPT + { + if (m_addrResult) + { + ::FreeAddrInfoW(m_addrResult); + } + } + + addr_info(const addr_info&) = delete; + addr_info& operator=(const addr_info&) = delete; + + addr_info(addr_info&& rhs) WI_NOEXCEPT + { + m_addrResult = rhs.m_addrResult; + rhs.m_addrResult = nullptr; + } + + addr_info& operator=(addr_info&& rhs) WI_NOEXCEPT + { + if (this != &rhs) + { + if (m_addrResult) + { + ::FreeAddrInfoW(m_addrResult); + } + m_addrResult = rhs.m_addrResult; + rhs.m_addrResult = nullptr; + } + + return *this; + } + + class iterator + { + public: + // defining iterator_traits allows STL functions to be used with this iterator class. + // Notice this is a forward_iterator + // - does not support random-access (e.g. vector::iterator) + // - does not support bidirectional access (e.g. list::iterator) +#if defined(_ITERATOR_) || defined(WIL_DOXYGEN) + using iterator_category = ::std::forward_iterator_tag; +#endif + using value_type = socket_address; + using difference_type = size_t; + using distance_type = size_t; + using pointer = socket_address*; + using reference = socket_address&; + + iterator(const ADDRINFOW* addr_info) WI_NOEXCEPT : + m_addr_info(const_cast(addr_info)) + { + // must const cast so we can re-use this pointer as we walk the list + if (m_addr_info) + { + m_socket_address.set_sockaddr(m_addr_info->ai_addr, m_addr_info->ai_addrlen); + } + } + + ~iterator() WI_NOEXCEPT = default; + iterator(const iterator&) WI_NOEXCEPT = default; + iterator& operator=(const iterator&) WI_NOEXCEPT = default; + iterator(iterator&&) WI_NOEXCEPT = default; + iterator& operator=(iterator&&) WI_NOEXCEPT = default; + + const socket_address& operator*() const WI_NOEXCEPT + { + return m_socket_address; + } + + const socket_address& operator*() WI_NOEXCEPT + { + return m_socket_address; + } + + const socket_address* operator->() const WI_NOEXCEPT + { + return &m_socket_address; + } + + const socket_address* operator->() WI_NOEXCEPT + { + return &m_socket_address; + } + + [[nodiscard]] bool operator==(const iterator& rhs) const WI_NOEXCEPT + { + return m_addr_info == rhs.m_addr_info; + } + + [[nodiscard]] bool operator!=(const iterator& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + // pre-increment + iterator& operator++() WI_NOEXCEPT + { + this->operator+=(1); + return *this; + } + + // increment by integer + iterator& operator+=(size_t offset) + { + for (size_t count = 0; count < offset; ++count) + { + WI_ASSERT(m_addr_info); + if (m_addr_info) + { + m_addr_info = m_addr_info->ai_next; + if (m_addr_info) + { + m_socket_address.set_sockaddr(m_addr_info->ai_addr, m_addr_info->ai_addrlen); + } + else + { + m_socket_address.reset(); + } + } + } + + return *this; + } + + // not supporting post-increment - which would require copy-construction + iterator operator++(int) = delete; + + private: + // non-ownership of this pointer - the parent class must outlive the iterator + ADDRINFOW* m_addr_info{nullptr}; + socket_address m_socket_address{}; + }; + + iterator begin() const WI_NOEXCEPT + { + return {m_addrResult}; + } + + iterator end() const WI_NOEXCEPT + { + return {nullptr}; + } + + private: + ADDRINFOW* m_addrResult{}; + int m_lastError{}; + }; + + //! wil function to capture resolving a name to a set of IP addresses + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + //! the returned RAII object exposes get_last_error() to check for errors + inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT + { + int lastError = 0; + ADDRINFOW* addrResult{}; + if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) { - return c_sockaddr_size; + lastError = ::WSAGetLastError(); } - private: - static constexpr int c_sockaddr_size = sizeof(SOCKADDR_INET); - SOCKADDR_INET m_sockaddr{}; - }; + return {addrResult, lastError}; + } - // When using dual-mode sockets, one might need to connect to a target IPv4 address. - // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address - inline socket_address map_dual_mode_4to6(const socket_address& inV4) WI_NOEXCEPT + //! wil function to capture resolving the local machine to its set of IP addresses + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + //! the returned RAII object exposes get_last_error() to check for errors + inline ::wil::networking::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT { - constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; + return ::wil::networking::resolve_name_nothrow(L""); + } - socket_address outV6{&v4MappedPrefix, inV4.port()}; + //! wil function to capture resolving the local-host addresses + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + //! the returned RAII object exposes get_last_error() to check for errors + inline ::wil::networking::addr_info resolve_localhost_addresses_nothrow() WI_NOEXCEPT + { + return ::wil::networking::resolve_name_nothrow(L"localhost"); + } - auto* const pIn6Addr = outV6.in6_addr(); - const auto* const pIn4Addr = inV4.in_addr(); - pIn6Addr->u.Byte[12] = pIn4Addr->S_un.S_un_b.s_b1; - pIn6Addr->u.Byte[13] = pIn4Addr->S_un.S_un_b.s_b2; - pIn6Addr->u.Byte[14] = pIn4Addr->S_un.S_un_b.s_b3; - pIn6Addr->u.Byte[15] = pIn4Addr->S_un.S_un_b.s_b4; +#if defined(WIL_ENABLE_EXCEPTIONS) + //! wil function to capture resolving a name to a set of IP addresses, throwing on error + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + inline ::wil::networking::addr_info resolve_name(_In_ PCWSTR name) + { + ADDRINFOW* addrResult{}; + if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) + { + THROW_WIN32(::WSAGetLastError()); + } - return outV6; + return {addrResult, NO_ERROR}; } - // non-member swap - inline void swap(socket_address& lhs, socket_address& rhs) WI_NOEXCEPT + //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + inline ::wil::networking::addr_info resolve_local_addresses() WI_NOEXCEPT { - lhs.swap(rhs); + return ::wil::networking::resolve_name(L""); + } + + //! wil function to capture resolving the local-host addresses, throwing on error + //! returning an RAII object containing the results + //! the returned RAII object exposes iterator semantics to walk the results + inline ::wil::networking::addr_info resolve_localhost_addresses() WI_NOEXCEPT + { + return ::wil::networking::resolve_name(L"localhost"); } +#endif + // + // socket_address definitions + // inline socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT { - m_sockaddr.si_family = family; + reset(family); } template @@ -627,7 +681,7 @@ namespace networking inline socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { - set_sockaddr(addr->lpSockaddr, addr->iSockaddrLength); + set_sockaddr(addr); } inline socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT @@ -644,10 +698,10 @@ namespace networking set_port(port); } -#ifdef WIL_ENABLE_EXCEPTIONS - inline socket_address::socket_address(const PCWSTR addr, unsigned short port) WI_NOEXCEPT +#if defined(WIL_ENABLE_EXCEPTIONS) + inline socket_address::socket_address(PCWSTR addr, unsigned short port) { - THROW_IF_FAILED(set_address_nothrow(addr)); + set_sockaddr(addr); set_port(port); } #endif @@ -666,6 +720,7 @@ namespace networking // don't compare the padding at the end of the SOCKADDR_IN return ::memcmp(&lhs.m_sockaddr.Ipv4, &rhs.m_sockaddr.Ipv4, sizeof(SOCKADDR_IN) - sizeof(SOCKADDR_IN::sin_zero)) == 0; } + return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) == 0; } @@ -677,41 +732,80 @@ namespace networking inline bool socket_address::operator<(const socket_address& rhs) const WI_NOEXCEPT { const auto& lhs = *this; + if (lhs.family() != rhs.family()) { return lhs.family() < rhs.family(); } - if (lhs.family() == AF_INET) + // for operator<, we cannot just memcmp the raw sockaddr values - as they are in network-byte order + // we have to first convert back to host-byte order to do comparisons + // else the user will see odd behavior, like 1.1.1.1 < 0.0.0.0 (which won't make senses) + switch (lhs.family()) + { + case AF_INET: { - // compare the address-only first + // compare the address first auto comparison = ::memcmp(lhs.in_addr(), rhs.in_addr(), sizeof(IN_ADDR)); if (comparison != 0) { return comparison < 0; } + // then compare the port (host-byte-order) - return lhs.port() < rhs.port(); - } + // only resolve the ntohs() once + const auto lhs_port = lhs.port(); + const auto rhs_port = rhs.port(); + if (lhs_port != rhs_port) + { + return lhs_port < rhs_port; + } - // compare the address-only first - auto comparison = ::memcmp(lhs.in6_addr(), rhs.in6_addr(), sizeof(IN6_ADDR)); - if (comparison != 0) - { - return comparison < 0; + return true; } - // then compare the scope_id of the address - if (lhs.scope_id() != rhs.scope_id()) + + case AF_INET6: { - return lhs.scope_id() < rhs.scope_id(); + // compare the address first + auto comparison = ::memcmp(lhs.in6_addr(), rhs.in6_addr(), sizeof(IN6_ADDR)); + if (comparison != 0) + { + return comparison < 0; + } + + // then compare the port (host-byte-order) + // only resolve the ntohs() once + const auto lhs_port = lhs.port(); + const auto rhs_port = rhs.port(); + if (lhs_port != rhs_port) + { + return lhs_port < rhs_port; + } + + // then compare the scope_id of the address + const auto lhs_scope_id = lhs.scope_id(); + const auto rhs_scope_id = rhs.scope_id(); + if (lhs_scope_id != rhs_scope_id) + { + return lhs_scope_id < rhs_scope_id; + } + + // then compare flow_info + const auto lhs_flow_info = lhs.flow_info(); + const auto rhs_flow_info = rhs.flow_info(); + if (lhs_flow_info != rhs_flow_info) + { + return lhs_flow_info < rhs_flow_info; + } + + return true; } - // then compare flow_info - if (lhs.flow_info() != rhs.flow_info()) - { - return lhs.flow_info() < rhs.flow_info(); + + default: + // if not AF_INET or AF_INET6, and families don't match + // then just raw memcmp the largest field of the union (v6) + return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) < 0; } - // then compare the port (host-byte-order) - return lhs.port() < rhs.port(); } inline bool socket_address::operator>(const socket_address& rhs) const WI_NOEXCEPT @@ -722,54 +816,60 @@ namespace networking inline void socket_address::swap(socket_address& addr) WI_NOEXCEPT { SOCKADDR_INET tempAddr{}; - ::CopyMemory(&tempAddr, &addr.m_sockaddr, c_sockaddr_size); - ::CopyMemory(&addr.m_sockaddr, &m_sockaddr, c_sockaddr_size); - ::CopyMemory(&m_sockaddr, &tempAddr, c_sockaddr_size); + ::memcpy_s(&tempAddr, sizeof(tempAddr), &addr.m_sockaddr, sizeof(addr.m_sockaddr)); + ::memcpy_s(&addr.m_sockaddr, sizeof(addr.m_sockaddr), &m_sockaddr, sizeof(m_sockaddr)); + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), &tempAddr, sizeof(tempAddr)); } inline void socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT { - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + WI_ASSERT(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); + + ::memset(&m_sockaddr, 0, socket_address::length); m_sockaddr.si_family = family; } template void socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT { - const size_t length = static_cast(inLength) < c_sockaddr_size ? inLength : c_sockaddr_size; - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - ::CopyMemory(&m_sockaddr, addr, length); + WI_ASSERT(static_cast(inLength) <= socket_address::length); + + ::memset(&m_sockaddr, 0, socket_address::length); + if (addr) + { + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, inLength); + } } inline void socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT { - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - ::CopyMemory(&m_sockaddr.Ipv4, addr, sizeof(SOCKADDR_IN)); + ::memset(&m_sockaddr, 0, socket_address::length); + ::memcpy_s(&m_sockaddr.Ipv4, sizeof(m_sockaddr.Ipv4), addr, sizeof(*addr)); } inline void socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT { - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - ::CopyMemory(&m_sockaddr.Ipv6, addr, sizeof(SOCKADDR_IN6)); + ::memset(&m_sockaddr, 0, socket_address::length); + ::memcpy_s(&m_sockaddr.Ipv6, sizeof(m_sockaddr.Ipv6), addr, sizeof(*addr)); } inline void socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT { - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); - ::CopyMemory(&m_sockaddr, addr, sizeof(SOCKADDR_INET)); + ::memset(&m_sockaddr, 0, socket_address::length); + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, sizeof(*addr)); } inline void socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { FAIL_FAST_IF_MSG( - addr->lpSockaddr && addr->iSockaddrLength > c_sockaddr_size, + addr->lpSockaddr && addr->iSockaddrLength > socket_address::length, "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", addr->iSockaddrLength); - ::ZeroMemory(&m_sockaddr, c_sockaddr_size); + ::memset(&m_sockaddr, 0, socket_address::length); if (addr->lpSockaddr) { - ::CopyMemory(&m_sockaddr, addr->lpSockaddr, addr->iSockaddrLength); + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr->lpSockaddr, addr->iSockaddrLength); } } @@ -785,10 +885,11 @@ namespace networking case AF_INET6: return ::IN6_IS_ADDR_LINKLOCAL(in6_addr()); - } - WI_ASSERT_MSG(false, "Unknown address family"); - return false; + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } } inline bool socket_address::is_address_loopback() const WI_NOEXCEPT @@ -803,13 +904,14 @@ namespace networking case AF_INET6: return ::IN6_IS_ADDR_LOOPBACK(in6_addr()); - } - WI_ASSERT_MSG(false, "Unknown address family"); - return false; + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } } - inline NL_ADDRESS_TYPE socket_address::get_address_type() const WI_NOEXCEPT + inline NL_ADDRESS_TYPE socket_address::address_type() const WI_NOEXCEPT { switch (family()) { @@ -821,17 +923,20 @@ namespace networking case AF_INET6: return ::Ipv6AddressType(reinterpret_cast(in6_addr())); - } - WI_ASSERT_MSG(false, "Unknown address family"); - return NlatInvalid; + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return NlatInvalid; + } } inline void socket_address::set_port(USHORT port) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET || family() == AF_INET6); + // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); + // port values in a sockaddr are always in network-byte order m_sockaddr.Ipv4.sin_port = ::htons(port); } @@ -839,18 +944,20 @@ namespace networking inline void socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); + if (family() == AF_INET6) { - m_sockaddr.Ipv6.sin6_scope_id = scopeId; + m_sockaddr.Ipv6.sin6_scope_id = ::htonl(scopeId); } } inline void socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); + if (family() == AF_INET6) { - m_sockaddr.Ipv6.sin6_flowinfo = flowInfo; + m_sockaddr.Ipv6.sin6_flowinfo = ::htonl(flowInfo); } } @@ -861,10 +968,12 @@ namespace networking inline void socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT { + WI_ASSERT(family == AF_INET || family == AF_INET6); + // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); + const auto original_port = m_sockaddr.Ipv4.sin_port; - WI_ASSERT(family == AF_INET || family == AF_INET6); reset(family); m_sockaddr.Ipv4.sin_port = original_port; } @@ -878,12 +987,13 @@ namespace networking { // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); + const auto original_port = m_sockaddr.Ipv4.sin_port; reset(family); switch (family) { case AF_INET: - m_sockaddr.Ipv4.sin_addr.s_addr = INADDR_LOOPBACK; + m_sockaddr.Ipv4.sin_addr.s_addr = ::htonl(INADDR_LOOPBACK); break; case AF_INET6: m_sockaddr.Ipv6.sin6_addr = {{IN6ADDR_LOOPBACK_INIT}}; @@ -896,8 +1006,9 @@ namespace networking inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT { - const auto original_port = m_sockaddr.Ipv4.sin_port; WI_ASSERT(family() == AF_INET); + + const auto original_port = m_sockaddr.Ipv4.sin_port; reset(AF_INET); m_sockaddr.Ipv4.sin_addr.s_addr = addr->s_addr; m_sockaddr.Ipv4.sin_port = original_port; @@ -905,17 +1016,36 @@ namespace networking inline void socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT { - const auto original_port = m_sockaddr.Ipv6.sin6_port; WI_ASSERT(family() == AF_INET6); + + const auto original_port = m_sockaddr.Ipv6.sin6_port; reset(AF_INET6); m_sockaddr.Ipv6.sin6_addr = *addr; m_sockaddr.Ipv6.sin6_port = original_port; } - inline HRESULT socket_address::set_address_nothrow(SOCKET s) WI_NOEXCEPT +#if defined(WIL_ENABLE_EXCEPTIONS) + inline void socket_address::set_sockaddr(SOCKET s) + { + THROW_IF_FAILED(set_sockaddr_nothrow(s)); + } + + inline void socket_address::set_sockaddr(PCWSTR address) + { + THROW_IF_FAILED(set_sockaddr_nothrow(address)); + } + + inline void socket_address::set_sockaddr(PCSTR address) + { + THROW_IF_FAILED(set_sockaddr_nothrow(address)); + } +#endif + + inline HRESULT socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT { reset(AF_UNSPEC); - auto nameLength = length(); + + auto nameLength = socket_address::length; auto error = ::getsockname(s, sockaddr(), &nameLength); if (error != 0) { @@ -925,47 +1055,57 @@ namespace networking return S_OK; } - inline HRESULT socket_address::set_address_nothrow(_In_ PCWSTR wszAddr) WI_NOEXCEPT + inline HRESULT socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT { - reset(AF_UNSPEC); PCWSTR terminator_unused; + + reset(AF_INET); constexpr BOOLEAN strict_string = TRUE; - if (RtlIpv4StringToAddressW(wszAddr, strict_string, &terminator_unused, in_addr()) == 0) + if (RtlIpv4StringToAddressW(address, strict_string, &terminator_unused, in_addr()) == 0) { m_sockaddr.si_family = AF_INET; return S_OK; } - if (RtlIpv6StringToAddressW(wszAddr, &terminator_unused, in6_addr()) == 0) + + reset(AF_INET6); + if (RtlIpv6StringToAddressW(address, &terminator_unused, in6_addr()) == 0) { m_sockaddr.si_family = AF_INET6; return S_OK; } + + reset(AF_UNSPEC); return E_INVALIDARG; } - inline HRESULT socket_address::set_address_nothrow(_In_ PCSTR szAddr) WI_NOEXCEPT + inline HRESULT socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT { - reset(AF_UNSPEC); PCSTR terminator_unused; + + reset(AF_INET); constexpr BOOLEAN strict_string = TRUE; - if (RtlIpv4StringToAddressA(szAddr, strict_string, &terminator_unused, in_addr()) == 0) + if (RtlIpv4StringToAddressA(address, strict_string, &terminator_unused, in_addr()) == 0) { m_sockaddr.si_family = AF_INET; return S_OK; } - if (RtlIpv6StringToAddressA(szAddr, &terminator_unused, in6_addr()) == 0) + + reset(AF_INET6); + if (RtlIpv6StringToAddressA(address, &terminator_unused, in6_addr()) == 0) { m_sockaddr.si_family = AF_INET6; return S_OK; } + + reset(AF_UNSPEC); return E_INVALIDARG; } -#if defined(_STRING_) || defined(WIL_DOXYGEN) +#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) inline std::wstring socket_address::write_address() const { socket_address_wstring returnString{}; - write_address_nothrow(returnString); + THROW_IF_FAILED(write_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } @@ -973,22 +1113,30 @@ namespace networking inline HRESULT socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { - ::ZeroMemory(address, sizeof(socket_address_wstring)); + ::memset(address, 0, sizeof(socket_address_wstring)); - const void* const pAddr = family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr); + const void* const pAddr = family() == AF_INET + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr); // the last param to InetNtopW is # of characters, not bytes - return nullptr != InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN); + const auto* error_value = ::InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN); + if (error_value == nullptr) + { + const auto gle = ::WSAGetLastError(); + RETURN_WIN32(gle); + } + return S_OK; } inline HRESULT socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { - ::ZeroMemory(address, sizeof(socket_address_string)); + ::memset(address, 0, sizeof(socket_address_string)); - const void* const pAddr = family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr); + const void* const pAddr = family() == AF_INET + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr); // the last param to InetNtopA is # of characters, not bytes - const auto* error_value = InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN); + const auto* error_value = ::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN); if (error_value == nullptr) { const auto gle = ::WSAGetLastError(); @@ -997,11 +1145,11 @@ namespace networking return S_OK; } -#if defined(_STRING_) || defined(WIL_DOXYGEN) +#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) inline std::wstring socket_address::write_complete_address() const { socket_address_wstring returnString{}; - write_complete_address_nothrow(returnString); + THROW_IF_FAILED(write_complete_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } @@ -1009,10 +1157,15 @@ namespace networking inline HRESULT socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { - ::ZeroMemory(address, sizeof(socket_address_wstring)); + ::memset(address, 0, sizeof(socket_address_wstring)); // addressLength == # of chars, not bytes DWORD addressLength = INET6_ADDRSTRLEN; - if (::WSAAddressToStringW(const_cast(sockaddr()), c_sockaddr_size, nullptr, address, &addressLength) != 0) + if (::WSAAddressToStringW( + const_cast(sockaddr()), + socket_address::length, + nullptr, + address, + &addressLength) != 0) { const auto gle = ::WSAGetLastError(); RETURN_WIN32(gle); @@ -1020,13 +1173,18 @@ namespace networking return S_OK; } -// the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API -#ifdef _WINSOCK_DEPRECATED_NO_WARNINGS + // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API +#if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) inline HRESULT socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { - ::ZeroMemory(address, sizeof(socket_address_string)); + ::memset(address, 0, sizeof(socket_address_string)); DWORD addressLength = INET6_ADDRSTRLEN; - if (::WSAAddressToStringA(const_cast(sockaddr()), c_sockaddrSize, nullptr, address, &addressLength) != 0) + if (::WSAAddressToStringA( + const_cast(sockaddr()), + socket_address::length, + nullptr, + address, + &addressLength) != 0) { const auto gle = ::WSAGetLastError(); RETURN_WIN32(gle); @@ -1047,9 +1205,9 @@ namespace networking case AF_UNSPEC: return 0; case AF_INET: - return ntohs(m_sockaddr.Ipv4.sin_port); + return ::ntohs(m_sockaddr.Ipv4.sin_port); case AF_INET6: - return ntohs(m_sockaddr.Ipv6.sin6_port); + return ::ntohs(m_sockaddr.Ipv6.sin6_port); default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; @@ -1061,11 +1219,11 @@ namespace networking switch (family()) { case AF_UNSPEC: - // fallthrough + // fallthrough case AF_INET: return 0; case AF_INET6: - return m_sockaddr.Ipv6.sin6_flowinfo; + return ::ntohl(m_sockaddr.Ipv6.sin6_flowinfo); default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; @@ -1077,11 +1235,11 @@ namespace networking switch (family()) { case AF_UNSPEC: - // fallthrough + // fallthrough case AF_INET: return 0; case AF_INET6: - return m_sockaddr.Ipv6.sin6_scope_id; + return ::ntohl(m_sockaddr.Ipv6.sin6_scope_id); default: WI_ASSERT_MSG(false, "Unknown address family"); return 0; @@ -1095,11 +1253,13 @@ namespace networking inline SOCKADDR_IN* socket_address::sockaddr_in() WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } inline SOCKADDR_IN6* socket_address::sockaddr_in6() WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } @@ -1110,11 +1270,13 @@ namespace networking inline IN_ADDR* socket_address::in_addr() WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } inline IN6_ADDR* socket_address::in6_addr() WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } @@ -1125,11 +1287,13 @@ namespace networking inline const SOCKADDR_IN* socket_address::sockaddr_in() const WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } inline const SOCKADDR_IN6* socket_address::sockaddr_in6() const WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } @@ -1140,14 +1304,182 @@ namespace networking inline const IN_ADDR* socket_address::in_addr() const WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } inline const IN6_ADDR* socket_address::in6_addr() const WI_NOEXCEPT { + WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } + + // + // definitions for winsock_extension_function_table + // + inline winsock_extension_function_table winsock_extension_function_table::load() WI_NOEXCEPT + { + winsock_extension_function_table table{}; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) + { + return table; + } + + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + return table; + } + + // walk WINSOCK_EXTENSION_FUNCTION_TABLE to load each function pointer + constexpr auto function_table_length = sizeof(table.f) / sizeof(void*); + static_assert(function_table_length == 8); + + bool successfully_loaded = true; + for (auto fnLoop = 0ul; successfully_loaded && fnLoop < function_table_length; ++fnLoop) + { + void* functionPtr{}; + GUID extensionGuid{}; + + switch (fnLoop) + { + case 0: + { + constexpr GUID acceptex_guid = WSAID_ACCEPTEX; + extensionGuid = acceptex_guid; + functionPtr = &table.f.AcceptEx; + break; + } + case 1: + { + constexpr GUID connectex_guid = WSAID_CONNECTEX; + extensionGuid = connectex_guid; + functionPtr = &table.f.ConnectEx; + break; + } + case 2: + { + constexpr GUID disconnectex_guid = WSAID_DISCONNECTEX; + extensionGuid = disconnectex_guid; + functionPtr = &table.f.DisconnectEx; + break; + } + case 3: + { + constexpr GUID getacceptexsockaddrs_guid = WSAID_GETACCEPTEXSOCKADDRS; + extensionGuid = getacceptexsockaddrs_guid; + functionPtr = &table.f.GetAcceptExSockaddrs; + break; + } + case 4: + { + constexpr GUID transmitfile_guid = WSAID_TRANSMITFILE; + extensionGuid = transmitfile_guid; + functionPtr = &table.f.TransmitFile; + break; + } + case 5: + { + constexpr GUID transmitpackets_guid = WSAID_TRANSMITPACKETS; + extensionGuid = transmitpackets_guid; + functionPtr = &table.f.TransmitPackets; + break; + } + case 6: + { + constexpr GUID wsarecvmsg_guid = WSAID_WSARECVMSG; + extensionGuid = wsarecvmsg_guid; + functionPtr = &table.f.WSARecvMsg; + break; + } + case 7: + { + constexpr GUID wsasendmsg_guid = WSAID_WSASENDMSG; + extensionGuid = wsasendmsg_guid; + functionPtr = &table.f.WSASendMsg; + break; + } + + default: + FAIL_FAST(); + } + + constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(void*)}; + DWORD unused_bytes; + if (::WSAIoctl( + localSocket.get(), + controlCode, + &extensionGuid, + DWORD{sizeof(extensionGuid)}, + functionPtr, + bytes, + &unused_bytes, + nullptr, + nullptr) != 0) + { + const auto gle = ::WSAGetLastError(); + LOG_IF_WIN32_ERROR(gle); + + // if any failed to be found, something is very broken + // all should load, or all should fail + ::memset(&table.f, 0, sizeof(table.f)); + successfully_loaded = false; + } + } + + return table; + } + + // + // definitions for rio_extension_function_table + // + inline rio_extension_function_table rio_extension_function_table::load() WI_NOEXCEPT + { + rio_extension_function_table table{}; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) + { + return table; + } + + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + return table; + } + + constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(table.f)}; + GUID rioGuid = WSAID_MULTIPLE_RIO; + + ::memset(&table.f, 0, bytes); + table.f.cbSize = bytes; + + DWORD unused_bytes; + if (::WSAIoctl( + localSocket.get(), + controlCode, + &rioGuid, + DWORD{sizeof(rioGuid)}, + &table.f, + bytes, + &unused_bytes, + nullptr, + nullptr) != 0) + { + const auto gle = ::WSAGetLastError(); + LOG_IF_WIN32_ERROR(gle); + + ::memset(&table.f, 0, bytes); + } + return table; + } + } // namespace networking } // namespace wil -#endif // __WIL_NETWORKING_INCLUDED +#endif // __WIL_NETWORKING_INCLUDED \ No newline at end of file diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index c1e674bb..073e4922 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -1,32 +1,58 @@ #include "pch.h" +#include +#include +#include +#include +#include + +// set this to give access to all functions +#define _WINSOCK_DEPRECATED_NO_WARNINGS #include #include "common.h" -constexpr auto* Test_in_addr_string = L"1.1.1.1"; -in_addr Test_in_addr{}; -constexpr auto* Test_in_addr_string2 = L"1.1.1.2"; -in_addr Test_in_addr2{}; -constexpr auto* Test_in6_addr_string = L"2001::1:1:1:1"; -in6_addr Test_in6_addr{}; -constexpr auto* Test_in6_addr_string2 = L"2001::1:1:1:2"; -in6_addr Test_in6_addr2{}; - -constexpr auto* Test_linklocal_in_addr_string = L"169.254.111.222"; -in_addr Test_linklocal_in_addr{}; -constexpr auto* Test_linklocal_in6_addr_string = L"fe80::1:1:1:1"; -in6_addr Test_linklocal_in6_addr{}; - -constexpr auto* Test_any_in_addr_string = L"0.0.0.0"; -in_addr Test_any_in_addr{}; -constexpr auto* Test_any_in6_addr_string = L"::"; -in6_addr Test_any_in6_addr{}; +constexpr char Test_in_addr_char_string[] = "1.1.1.1"; +constexpr wchar_t Test_in_addr_string[] = L"1.1.1.1"; +static in_addr Test_in_addr{}; +constexpr char Test_in_addr_char_string2[] = "1.1.1.2"; +constexpr wchar_t Test_in_addr_string2[] = L"1.1.1.2"; +static in_addr Test_in_addr2{}; +constexpr char Test_in6_addr_char_string[] = "2001::1:1:1:1"; +constexpr wchar_t Test_in6_addr_string[] = L"2001::1:1:1:1"; +static in6_addr Test_in6_addr{}; +constexpr char Test_in6_addr_char_string2[] = "2001::1:1:1:2"; +constexpr wchar_t Test_in6_addr_string2[] = L"2001::1:1:1:2"; +static in6_addr Test_in6_addr2{}; + +constexpr wchar_t Test_linklocal_in_addr_string[] = L"169.254.111.222"; +static in_addr Test_linklocal_in_addr{}; +constexpr wchar_t Test_linklocal_in6_addr_string[] = L"fe80::1:1:1111:2222"; +static in6_addr Test_linklocal_in6_addr{}; + +constexpr char Test_any_in_addr_char_string[] = "0.0.0.0"; +constexpr wchar_t Test_any_in_addr_string[] = L"0.0.0.0"; +constexpr char Test_any_in_addr_char_string_with_port[] = "0.0.0.0:12345"; +constexpr wchar_t Test_any_in_addr_string_with_port[] = L"0.0.0.0:12345"; +static in_addr Test_any_in_addr{}; +constexpr char Test_any_in6_addr_char_string[] = "::"; +constexpr wchar_t Test_any_in6_addr_string[] = L"::"; +constexpr char Test_any_in6_addr_char_string_with_port[] = "[::]:12345"; +constexpr wchar_t Test_any_in6_addr_string_with_port[] = L"[::]:12345"; +static in6_addr Test_any_in6_addr{}; + +constexpr wchar_t Test_loopback_in_addr_string[] = L"127.0.0.1"; +constexpr wchar_t Test_loopback_in_addr_string_with_port[] = L"127.0.0.1:12345"; +static in_addr Test_loopback_in_addr{}; +constexpr wchar_t Test_loopback_in6_addr_string[] = L"::1"; +constexpr wchar_t Test_loopback_in6_addr_string_with_port[] = L"[::1]:12345"; +static in6_addr Test_loopback_in6_addr{}; constexpr uint16_t TestPort = 12345; -INIT_ONCE SocketTestInit = INIT_ONCE_STATIC_INIT; -void InitTestAddresses() +static INIT_ONCE SocketTestInit = INIT_ONCE_STATIC_INIT; + +static void InitTestAddresses() { InitOnceExecuteOnce( &SocketTestInit, @@ -78,6 +104,18 @@ void InitTestAddresses() return FALSE; } + error = RtlIpv4StringToAddressW(Test_loopback_in_addr_string, TRUE, &terminator, &Test_loopback_in_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + + error = RtlIpv6StringToAddressW(Test_loopback_in6_addr_string, &terminator, &Test_loopback_in6_addr); + if (error != ERROR_SUCCESS) + { + return FALSE; + } + return TRUE; }, nullptr, @@ -87,10 +125,12 @@ void InitTestAddresses() TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") { // verify socket APIs fail without having called WSAStartup - const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - const auto gle = ::WSAGetLastError(); - REQUIRE(socket_test == INVALID_SOCKET); - REQUIRE(gle == WSANOTINITIALISED); + // i.e., WSAStartup was not called elsewhere in the test app + // since that would break the preconditions of this test case + const auto verify_socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + const auto verify_gle = ::WSAGetLastError(); + REQUIRE(verify_socket_test == INVALID_SOCKET); + REQUIRE(verify_gle == WSANOTINITIALISED); SECTION("Verifying _nothrow") { @@ -121,10 +161,223 @@ TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") #endif } +TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") +{ + using wil::networking::socket_address; + InitTestAddresses(); + + SECTION("socket_address(ADDRESS_FAMILY)") + { + socket_address default_addr; + REQUIRE(default_addr.family() == AF_UNSPEC); + REQUIRE(default_addr.address_type() == NlatUnspecified); + REQUIRE(!default_addr.is_address_linklocal()); + REQUIRE(!default_addr.is_address_loopback()); + + socket_address v4_addr{AF_INET}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnspecified); + REQUIRE(!v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in_addr_string == v4_addr.write_address()); +#endif + + socket_address v6_addr{AF_INET6}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnspecified); + REQUIRE(!v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in6_addr_string == v6_addr.write_address()); +#endif + } + + SECTION("socket_address(const SOCKADDR*, T)") + { + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + socket_address v4_addr{reinterpret_cast(&v4_test_sockaddr), sizeof(v4_test_sockaddr)}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw value are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(ULONG_MAX - 1); + v6_test_sockaddr.sin6_scope_id = htonl(ULONG_MAX - 1); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + socket_address v6_addr{reinterpret_cast(&v6_test_sockaddr), sizeof(v6_test_sockaddr)}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == ULONG_MAX - 1); + REQUIRE(v6_addr.scope_id() == ULONG_MAX - 1); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + } + + SECTION("socket_address(const SOCKADDR_IN*)") + { + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + socket_address v4_addr{&v4_test_sockaddr}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + } + + SECTION("socket_address(const SOCKADDR_IN6*)") + { + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw value are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(ULONG_MAX - 1); + v6_test_sockaddr.sin6_scope_id = htonl(ULONG_MAX - 1); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + socket_address v6_addr{&v6_test_sockaddr}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == ULONG_MAX - 1); + REQUIRE(v6_addr.scope_id() == ULONG_MAX - 1); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + } + + SECTION("socket_address(const SOCKADDR_INET*)") + { + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + SOCKADDR_INET v4_inet_addr{}; + memcpy(&v4_inet_addr.Ipv4, &v4_test_sockaddr, sizeof(SOCKADDR_IN)); + + socket_address v4_addr{&v4_inet_addr}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + REQUIRE(0 == memcmp(&v4_inet_addr, v4_addr.sockaddr_inet(), sizeof(SOCKADDR_INET))); + + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw value are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(ULONG_MAX - 1); + v6_test_sockaddr.sin6_scope_id = htonl(ULONG_MAX - 1); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + SOCKADDR_INET v6_inet_addr{}; + memcpy(&v6_inet_addr.Ipv6, &v6_test_sockaddr, sizeof(SOCKADDR_IN6)); + + socket_address v6_addr{&v6_inet_addr}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == ULONG_MAX - 1); + REQUIRE(v6_addr.scope_id() == ULONG_MAX - 1); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + REQUIRE(0 == memcmp(&v6_inet_addr, v6_addr.sockaddr_inet(), sizeof(SOCKADDR_INET))); + } + + SECTION("socket_address(const SOCKET_ADDRESS*)") + { + SOCKET_ADDRESS default_socketaddress{}; + socket_address default_addr{&default_socketaddress}; + REQUIRE(default_addr.family() == AF_UNSPEC); + REQUIRE(default_addr.address_type() == NlatUnspecified); + REQUIRE(!default_addr.is_address_linklocal()); + REQUIRE(!default_addr.is_address_loopback()); + + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + SOCKET_ADDRESS v4_socketaddress{}; + v4_socketaddress.lpSockaddr = reinterpret_cast(&v4_test_sockaddr); + v4_socketaddress.iSockaddrLength = sizeof(v4_test_sockaddr); + + socket_address v4_addr{&v4_socketaddress}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw value are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(ULONG_MAX - 1); + v6_test_sockaddr.sin6_scope_id = htonl(ULONG_MAX - 1); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + SOCKET_ADDRESS v6_socketaddress{}; + v6_socketaddress.lpSockaddr = reinterpret_cast(&v6_test_sockaddr); + v6_socketaddress.iSockaddrLength = sizeof(v6_test_sockaddr); + + socket_address v6_addr{&v6_socketaddress}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == ULONG_MAX - 1); + REQUIRE(v6_addr.scope_id() == ULONG_MAX - 1); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + } +} + TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::networking::socket_address::length() == sizeof(SOCKADDR_INET)); + REQUIRE(wil::networking::socket_address::length == sizeof(SOCKADDR_INET)); wil::networking::socket_address default_addr{}; @@ -148,28 +401,19 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; - SECTION("Default socket address properties") - { - REQUIRE(default_addr.family() == AF_UNSPEC); - REQUIRE(default_addr.get_address_type() == NlatUnspecified); - REQUIRE(!default_addr.is_address_linklocal()); - REQUIRE(!default_addr.is_address_loopback()); - REQUIRE(NlatUnspecified == default_addr.get_address_type()); - - REQUIRE(default_addr == default_addr); - } + using wil::networking::equals; SECTION("IPv4 in_addr properties") { REQUIRE(test_v4_addr.family() == AF_INET); - REQUIRE(test_v4_addr.get_address_type() == NlatUnicast); + REQUIRE(test_v4_addr.address_type() == NlatUnicast); REQUIRE(!test_v4_addr.is_address_linklocal()); REQUIRE(!test_v4_addr.is_address_loopback()); - REQUIRE(NlatUnicast == test_v4_addr.get_address_type()); - REQUIRE(NlatUnicast == test_v4_addr2.get_address_type()); + REQUIRE(NlatUnicast == test_v4_addr.address_type()); + REQUIRE(NlatUnicast == test_v4_addr2.address_type()); - REQUIRE(test_v4_addr.in_addr()->s_addr == Test_in_addr.s_addr); - REQUIRE(test_v4_addr2.in_addr()->s_addr == Test_in_addr2.s_addr); + REQUIRE(equals(*test_v4_addr.in_addr(), Test_in_addr)); + REQUIRE(equals(*test_v4_addr2.in_addr(), Test_in_addr2)); REQUIRE(test_v4_addr.port() == 0); REQUIRE(test_v4_addr.scope_id() == 0); REQUIRE(test_v4_addr.flow_info() == 0); @@ -185,13 +429,13 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 in_addr with port properties") { REQUIRE(test_v4_addr_with_port.family() == AF_INET); - REQUIRE(test_v4_addr_with_port.get_address_type() == NlatUnicast); + REQUIRE(test_v4_addr_with_port.address_type() == NlatUnicast); REQUIRE(!test_v4_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnicast == test_v4_addr_with_port.get_address_type()); + REQUIRE(NlatUnicast == test_v4_addr_with_port.address_type()); - REQUIRE(test_v4_addr_with_port.in_addr()->s_addr == Test_in_addr.s_addr); - REQUIRE(test_v4_addr_with_port.in_addr()->s_addr == test_v4_addr.in_addr()->s_addr); + REQUIRE(equals(*test_v4_addr_with_port.in_addr(), Test_in_addr)); + REQUIRE(equals(*test_v4_addr_with_port.in_addr(), *test_v4_addr.in_addr())); REQUIRE(test_v4_addr_with_port.port() == TestPort); REQUIRE(test_v4_addr_with_port.scope_id() == 0); REQUIRE(test_v4_addr_with_port.flow_info() == 0); @@ -207,14 +451,13 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 in6_addr properties") { REQUIRE(test_v6_addr.family() == AF_INET6); - REQUIRE(test_v6_addr.get_address_type() == NlatUnicast); + REQUIRE(test_v6_addr.address_type() == NlatUnicast); REQUIRE(!test_v6_addr.is_address_linklocal()); REQUIRE(!test_v6_addr.is_address_loopback()); - REQUIRE(NlatUnicast == test_v6_addr.get_address_type()); - REQUIRE(NlatUnicast == test_v6_addr2.get_address_type()); + REQUIRE(NlatUnicast == test_v6_addr2.address_type()); - REQUIRE(0 == memcmp(test_v6_addr.in6_addr(), &Test_in6_addr, sizeof(in6_addr))); - REQUIRE(0 == memcmp(test_v6_addr2.in6_addr(), &Test_in6_addr2, sizeof(in6_addr))); + REQUIRE(equals(*test_v6_addr.in6_addr(), Test_in6_addr)); + REQUIRE(equals(*test_v6_addr2.in6_addr(), Test_in6_addr2)); REQUIRE(test_v6_addr.port() == 0); REQUIRE(test_v6_addr.scope_id() == 0); REQUIRE(test_v6_addr.flow_info() == 0); @@ -232,13 +475,13 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 in6_addr with port properties") { REQUIRE(test_v6_addr_with_port.family() == AF_INET6); - REQUIRE(test_v6_addr_with_port.get_address_type() == NlatUnicast); + REQUIRE(test_v6_addr_with_port.address_type() == NlatUnicast); REQUIRE(!test_v6_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnicast == test_v6_addr_with_port.get_address_type()); + REQUIRE(NlatUnicast == test_v6_addr_with_port.address_type()); - REQUIRE(0 == memcmp(test_v6_addr_with_port.in6_addr(), &Test_in6_addr, sizeof(in6_addr))); - REQUIRE(0 == memcmp(test_v6_addr_with_port.in6_addr(), test_v6_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(equals(*test_v6_addr_with_port.in6_addr(), Test_in6_addr)); + REQUIRE(equals(*test_v6_addr_with_port.in6_addr(), *test_v6_addr.in6_addr())); REQUIRE(test_v6_addr_with_port.port() == TestPort); REQUIRE(test_v6_addr_with_port.scope_id() == 0); REQUIRE(test_v6_addr_with_port.flow_info() == 0); @@ -257,12 +500,11 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 link-local in_addr properties") { REQUIRE(test_v4_linklocal_addr.family() == AF_INET); - REQUIRE(test_v4_linklocal_addr.get_address_type() == NlatUnicast); + REQUIRE(test_v4_linklocal_addr.address_type() == NlatUnicast); REQUIRE(test_v4_linklocal_addr.is_address_linklocal()); REQUIRE(!test_v4_linklocal_addr.is_address_loopback()); - REQUIRE(NlatUnicast == test_v4_linklocal_addr.get_address_type()); - REQUIRE(test_v4_linklocal_addr.in_addr()->s_addr == Test_linklocal_in_addr.s_addr); + REQUIRE(equals(*test_v4_linklocal_addr.in_addr(), Test_linklocal_in_addr)); REQUIRE(test_v4_linklocal_addr.port() == 0); REQUIRE(test_v4_linklocal_addr.scope_id() == 0); REQUIRE(test_v4_linklocal_addr.flow_info() == 0); @@ -278,13 +520,12 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 link-local in_addr with port properties") { REQUIRE(test_v4_linklocal_addr_with_port.family() == AF_INET); - REQUIRE(test_v4_linklocal_addr_with_port.get_address_type() == NlatUnicast); + REQUIRE(test_v4_linklocal_addr_with_port.address_type() == NlatUnicast); REQUIRE(test_v4_linklocal_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_linklocal_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnicast == test_v4_linklocal_addr_with_port.get_address_type()); - REQUIRE(test_v4_linklocal_addr_with_port.in_addr()->s_addr == Test_linklocal_in_addr.s_addr); - REQUIRE(test_v4_linklocal_addr_with_port.in_addr()->s_addr == test_v4_linklocal_addr.in_addr()->s_addr); + REQUIRE(equals(*test_v4_linklocal_addr_with_port.in_addr(), Test_linklocal_in_addr)); + REQUIRE(equals(*test_v4_linklocal_addr_with_port.in_addr(), *test_v4_linklocal_addr.in_addr())); REQUIRE(test_v4_linklocal_addr_with_port.port() == TestPort); REQUIRE(test_v4_linklocal_addr_with_port.scope_id() == 0); REQUIRE(test_v4_linklocal_addr_with_port.flow_info() == 0); @@ -301,12 +542,11 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 link-local in6_addr properties") { REQUIRE(test_v6_linklocal_addr.family() == AF_INET6); - REQUIRE(test_v6_linklocal_addr.get_address_type() == NlatUnicast); + REQUIRE(test_v6_linklocal_addr.address_type() == NlatUnicast); REQUIRE(test_v6_linklocal_addr.is_address_linklocal()); REQUIRE(!test_v6_linklocal_addr.is_address_loopback()); - REQUIRE(NlatUnicast == test_v6_linklocal_addr.get_address_type()); - REQUIRE(0 == memcmp(test_v6_linklocal_addr.in6_addr(), &Test_linklocal_in6_addr, sizeof(in6_addr))); + REQUIRE(equals(*test_v6_linklocal_addr.in6_addr(), Test_linklocal_in6_addr)); REQUIRE(test_v6_linklocal_addr.port() == 0); REQUIRE(test_v6_linklocal_addr.scope_id() == 0); REQUIRE(test_v6_linklocal_addr.flow_info() == 0); @@ -324,13 +564,12 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 link-local in6_addr with port properties") { REQUIRE(test_v6_linklocal_addr_with_port.family() == AF_INET6); - REQUIRE(test_v6_linklocal_addr_with_port.get_address_type() == NlatUnicast); + REQUIRE(test_v6_linklocal_addr_with_port.address_type() == NlatUnicast); REQUIRE(test_v6_linklocal_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_linklocal_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnicast == test_v6_linklocal_addr_with_port.get_address_type()); - REQUIRE(0 == memcmp(test_v6_linklocal_addr_with_port.in6_addr(), &Test_linklocal_in6_addr, sizeof(in6_addr))); - REQUIRE(0 == memcmp(test_v6_linklocal_addr_with_port.in6_addr(), test_v6_linklocal_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(equals(*test_v6_linklocal_addr_with_port.in6_addr(), Test_linklocal_in6_addr)); + REQUIRE(equals(*test_v6_linklocal_addr_with_port.in6_addr(), *test_v6_linklocal_addr.in6_addr())); REQUIRE(test_v6_linklocal_addr_with_port.port() == TestPort); REQUIRE(test_v6_linklocal_addr_with_port.scope_id() == 0); REQUIRE(test_v6_linklocal_addr_with_port.flow_info() == 0); @@ -349,12 +588,11 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 any-addr in_addr properties") { REQUIRE(test_v4_any_addr.family() == AF_INET); - REQUIRE(test_v4_any_addr.get_address_type() == NlatUnspecified); + REQUIRE(test_v4_any_addr.address_type() == NlatUnspecified); REQUIRE(!test_v4_any_addr.is_address_linklocal()); REQUIRE(!test_v4_any_addr.is_address_loopback()); - REQUIRE(NlatUnspecified == test_v4_any_addr.get_address_type()); - REQUIRE(test_v4_any_addr.in_addr()->s_addr == Test_any_in_addr.s_addr); + REQUIRE(equals(*test_v4_any_addr.in_addr(), Test_any_in_addr)); REQUIRE(test_v4_any_addr.port() == 0); REQUIRE(test_v4_any_addr.scope_id() == 0); REQUIRE(test_v4_any_addr.flow_info() == 0); @@ -374,13 +612,12 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv4 any-addr in_addr with port properties") { REQUIRE(test_v4_any_addr_with_port.family() == AF_INET); - REQUIRE(test_v4_any_addr_with_port.get_address_type() == NlatUnspecified); + REQUIRE(test_v4_any_addr_with_port.address_type() == NlatUnspecified); REQUIRE(!test_v4_any_addr_with_port.is_address_linklocal()); REQUIRE(!test_v4_any_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnspecified == test_v4_any_addr_with_port.get_address_type()); - REQUIRE(test_v4_any_addr_with_port.in_addr()->s_addr == Test_any_in_addr.s_addr); - REQUIRE(test_v4_any_addr_with_port.in_addr()->s_addr == test_v4_any_addr.in_addr()->s_addr); + REQUIRE(equals(*test_v4_any_addr_with_port.in_addr(), Test_any_in_addr)); + REQUIRE(equals(*test_v4_any_addr_with_port.in_addr(), *test_v4_any_addr.in_addr())); REQUIRE(test_v4_any_addr_with_port.port() == TestPort); REQUIRE(test_v4_any_addr_with_port.scope_id() == 0); REQUIRE(test_v4_any_addr_with_port.flow_info() == 0); @@ -401,12 +638,11 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 any-addr in6_addr properties") { REQUIRE(test_v6_any_addr.family() == AF_INET6); - REQUIRE(test_v6_any_addr.get_address_type() == NlatUnspecified); + REQUIRE(test_v6_any_addr.address_type() == NlatUnspecified); REQUIRE(!test_v6_any_addr.is_address_linklocal()); REQUIRE(!test_v6_any_addr.is_address_loopback()); - REQUIRE(NlatUnspecified == test_v6_any_addr.get_address_type()); - REQUIRE(0 == memcmp(test_v6_any_addr.in6_addr(), &Test_any_in6_addr, sizeof(in6_addr))); + REQUIRE(equals(*test_v6_any_addr.in6_addr(), Test_any_in6_addr)); REQUIRE(test_v6_any_addr.port() == 0); REQUIRE(test_v6_any_addr.scope_id() == 0); REQUIRE(test_v6_any_addr.flow_info() == 0); @@ -428,13 +664,12 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") SECTION("IPv6 any-addr in6_addr with port properties") { REQUIRE(test_v6_any_addr_with_port.family() == AF_INET6); - REQUIRE(test_v6_any_addr_with_port.get_address_type() == NlatUnspecified); + REQUIRE(test_v6_any_addr_with_port.address_type() == NlatUnspecified); REQUIRE(!test_v6_any_addr_with_port.is_address_linklocal()); REQUIRE(!test_v6_any_addr_with_port.is_address_loopback()); - REQUIRE(NlatUnspecified == test_v6_any_addr_with_port.get_address_type()); - REQUIRE(0 == memcmp(test_v6_any_addr_with_port.in6_addr(), &Test_any_in6_addr, sizeof(in6_addr))); - REQUIRE(0 == memcmp(test_v6_any_addr_with_port.in6_addr(), test_v6_any_addr.in6_addr(), sizeof(in6_addr))); + REQUIRE(equals(*test_v6_any_addr_with_port.in6_addr(), Test_any_in6_addr)); + REQUIRE(equals(*test_v6_any_addr_with_port.in6_addr(), *test_v6_any_addr.in6_addr())); REQUIRE(test_v6_any_addr_with_port.port() == TestPort); REQUIRE(test_v6_any_addr_with_port.scope_id() == 0); REQUIRE(test_v6_any_addr_with_port.flow_info() == 0); @@ -457,44 +692,102 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") { + using wil::networking::equals; + #ifdef WIL_ENABLE_EXCEPTIONS using wil::networking::socket_address; - REQUIRE(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.1"})); - REQUIRE(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.1"})); - - REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.2"}); - REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.2"})); - REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.2"}); - REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.2"})); - - REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"2.1.1.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"2.1.1.1"})); - REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"2.1.1.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"2.1.1.1"})); - - REQUIRE(socket_address{L"1.1.1.1"} > socket_address{L"0.0.0.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} < socket_address{L"0.0.0.1"})); - REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"0.0.0.1"}); - REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"0.0.0.1"})); - - REQUIRE(socket_address{L"1.1.1.1", 1} < socket_address{L"1.1.1.1", 2}); - REQUIRE(!(socket_address{L"1.1.1.1", 1} > socket_address{L"1.1.1.1", 2})); - REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"1.1.1.1", 2}); - REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"1.1.1.1", 2})); - - REQUIRE(socket_address{L"1.1.1.1", 1} > socket_address{L"0.0.0.0", 65535}); - REQUIRE(!(socket_address{L"1.1.1.1", 1} < socket_address{L"0.0.0.0", 65535})); - REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"0.0.0.0", 65535}); - REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"0.0.0.0", 65535})); + SECTION("verify v4 address comparisons") + { + // equal will be considered less-than + REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.1"})); + REQUIRE(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.1"})); + + REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.2"}); + REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.2"})); + REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.2"}); + REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.2"})); + + REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"2.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"2.1.1.1"})); + REQUIRE(socket_address{L"1.1.1.1"} != socket_address{L"2.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} == socket_address{L"2.1.1.1"})); + + REQUIRE(socket_address{L"1.0.0.0"} > socket_address{L"0.0.0.1"}); + REQUIRE(!(socket_address{L"1.0.0.0"} < socket_address{L"0.0.0.1"})); + REQUIRE(socket_address{L"1.0.0.0"} != socket_address{L"0.0.0.1"}); + REQUIRE(!(socket_address{L"1.0.0.0"} == socket_address{L"0.0.0.1"})); + + REQUIRE(socket_address{L"1.1.1.1", 1} < socket_address{L"1.1.1.1", 2}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} > socket_address{L"1.1.1.1", 2})); + REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"1.1.1.1", 2}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"1.1.1.1", 2})); + + REQUIRE(socket_address{L"1.1.1.1", 1} > socket_address{L"0.0.0.0", 65535}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} < socket_address{L"0.0.0.0", 65535})); + REQUIRE(socket_address{L"1.1.1.1", 1} != socket_address{L"0.0.0.0", 65535}); + REQUIRE(!(socket_address{L"1.1.1.1", 1} == socket_address{L"0.0.0.0", 65535})); + + REQUIRE(socket_address{L"254.254.254.254"} > socket_address{L"127.127.127.127"}); + REQUIRE(!(socket_address{L"254.254.254.254"} < socket_address{L"127.127.127.127"})); + REQUIRE(socket_address{L"254.254.254.254"} != socket_address{L"127.127.127.127"}); + REQUIRE(!(socket_address{L"254.254.254.254"} == socket_address{L"127.127.127.127"})); + } + + SECTION("verify v6 address comparisons") + { + // equal will be considered less-than + REQUIRE(socket_address{L"2001::1002"} < socket_address{L"2001::1002"}); + REQUIRE(!(socket_address{L"2001::1002"} > socket_address{L"2001::1002"})); + REQUIRE(socket_address{L"2001::1002"} == socket_address{L"2001::1002"}); + REQUIRE(!(socket_address{L"2001::1002"} != socket_address{L"2001::1002"})); + + REQUIRE(socket_address{L"2001::1002"} < socket_address{L"2001::1003"}); + REQUIRE(!(socket_address{L"2001::1002"} > socket_address{L"2001::1003"})); + REQUIRE(socket_address{L"2001::1002"} != socket_address{L"2001::1003"}); + REQUIRE(!(socket_address{L"2001::1002"} == socket_address{L"2001::1003"})); + + REQUIRE(socket_address{L"2001::1002"} > socket_address{L"1002::2001"}); + REQUIRE(!(socket_address{L"2001::1002"} < socket_address{L"1002::2001"})); + REQUIRE(socket_address{L"2001::1002"} != socket_address{L"1002::2001"}); + REQUIRE(!(socket_address{L"2001::1002"} == socket_address{L"1002::2001"})); + + REQUIRE(socket_address{L"2001::1002"} > socket_address{L"::1"}); + REQUIRE(!(socket_address{L"2001::1002"} < socket_address{L"::1"})); + REQUIRE(socket_address{L"2001::1002"} != socket_address{L"::1"}); + REQUIRE(!(socket_address{L"2001::1002"} == socket_address{L"::1"})); + + REQUIRE(socket_address{L"2001::1002", 1} < socket_address{L"2001::1002", 2}); + REQUIRE(!(socket_address{L"2001::1002", 1} > socket_address{L"2001::1002", 2})); + REQUIRE(socket_address{L"2001::1002", 1} != socket_address{L"2001::1002", 2}); + REQUIRE(!(socket_address{L"2001::1002", 1} == socket_address{L"2001::1002", 2})); + + socket_address lhs_scope_id_test{L"2001::1002", 1}; + lhs_scope_id_test.set_scope_id(10000); + socket_address rhs_scope_id_test{L"2001::1002", 1}; + rhs_scope_id_test.set_scope_id(100000); + REQUIRE(lhs_scope_id_test < rhs_scope_id_test); + REQUIRE(!(lhs_scope_id_test > rhs_scope_id_test)); + REQUIRE(lhs_scope_id_test != rhs_scope_id_test); + REQUIRE(!(lhs_scope_id_test == rhs_scope_id_test)); + + socket_address lhs_flow_info_test{L"2001::1002", 1}; + lhs_flow_info_test.set_flow_info(10000); + socket_address rhs_flow_info_test{L"2001::1002", 1}; + rhs_flow_info_test.set_flow_info(100000); + REQUIRE(lhs_flow_info_test < rhs_flow_info_test); + REQUIRE(!(lhs_flow_info_test > rhs_flow_info_test)); + REQUIRE(lhs_flow_info_test != rhs_flow_info_test); + REQUIRE(!(lhs_flow_info_test == rhs_flow_info_test)); + } #endif } TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::networking::socket_address::length() == sizeof(SOCKADDR_INET)); + REQUIRE(wil::networking::socket_address::length == sizeof(SOCKADDR_INET)); wil::networking::socket_address default_addr{}; @@ -518,84 +811,1466 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + wil::networking::socket_address test_v4_loopback_addr{&Test_loopback_in_addr}; + wil::networking::socket_address test_v4_loopback_addr_with_port{&Test_loopback_in_addr, TestPort}; + + wil::networking::socket_address test_v6_loopback_addr{&Test_loopback_in6_addr}; + wil::networking::socket_address test_v6_loopback_addr_with_port{&Test_loopback_in6_addr, TestPort}; + + // need WSAStartup called for some functions below + auto wsa_startup_tracking = wil::networking::WSAStartup_nothrow(); + REQUIRE(static_cast(wsa_startup_tracking)); + + using wil::networking::equals; + SECTION("verify set_address_any") { + const auto VerifyV4AnyAddress = [&](const wil::networking::socket_address& v4_address, bool with_port) { + wil::networking::socket_address_wstring any_address_test_string{}; + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(any_address_test_string))); + REQUIRE(0 == memcmp(Test_any_in_addr_string, any_address_test_string, sizeof(Test_any_in_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in_addr_string == v4_address.write_address()); +#endif + + ZeroMemory(any_address_test_string, sizeof(any_address_test_string)); + + REQUIRE(SUCCEEDED(v4_address.write_complete_address_nothrow(any_address_test_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_any_in_addr_string_with_port, any_address_test_string, sizeof(Test_any_in_addr_string_with_port))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in_addr_string_with_port == v4_address.write_complete_address()); +#endif + } + else + { + REQUIRE(0 == memcmp(Test_any_in_addr_string, any_address_test_string, sizeof(Test_any_in_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in_addr_string == v4_address.write_complete_address()); +#endif + } + + // also char* versions + wil::networking::socket_address_string any_address_test_char_string{}; + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(any_address_test_char_string))); + REQUIRE(0 == memcmp(Test_any_in_addr_char_string, any_address_test_char_string, sizeof(Test_any_in_addr_char_string))); + + ZeroMemory(any_address_test_char_string, sizeof(any_address_test_char_string)); + + REQUIRE(SUCCEEDED(v4_address.write_complete_address_nothrow(any_address_test_char_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_any_in_addr_char_string_with_port, any_address_test_char_string, sizeof(Test_any_in_addr_char_string_with_port))); + } + else + { + REQUIRE(0 == memcmp(Test_any_in_addr_char_string, any_address_test_char_string, sizeof(Test_any_in_addr_char_string))); + } + }; + + const auto VerifyV6AnyAddress = [&](const wil::networking::socket_address& v6_address, bool with_port) { + wil::networking::socket_address_wstring any_address_test_string{}; + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(any_address_test_string))); + REQUIRE(0 == memcmp(Test_any_in6_addr_string, any_address_test_string, sizeof(Test_any_in6_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in6_addr_string == v6_address.write_address()); +#endif + + ZeroMemory(any_address_test_string, sizeof(any_address_test_string)); + + REQUIRE(SUCCEEDED(v6_address.write_complete_address_nothrow(any_address_test_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_any_in6_addr_string_with_port, any_address_test_string, sizeof(Test_any_in6_addr_string_with_port))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in6_addr_string_with_port == v6_address.write_complete_address()); +#endif + } + else + { + REQUIRE(0 == memcmp(Test_any_in6_addr_string, any_address_test_string, sizeof(Test_any_in6_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_any_in6_addr_string == v6_address.write_complete_address()); +#endif + } + + // also char* versions + wil::networking::socket_address_string any_address_test_char_string{}; + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(any_address_test_char_string))); + REQUIRE(0 == memcmp(Test_any_in6_addr_char_string, any_address_test_char_string, sizeof(Test_any_in6_addr_char_string))); + + ZeroMemory(any_address_test_char_string, sizeof(any_address_test_char_string)); + + REQUIRE(SUCCEEDED(v6_address.write_complete_address_nothrow(any_address_test_char_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_any_in6_addr_char_string_with_port, any_address_test_char_string, sizeof(Test_any_in6_addr_char_string_with_port))); + } + else + { + REQUIRE(0 == memcmp(Test_any_in6_addr_char_string, any_address_test_char_string, sizeof(Test_any_in6_addr_char_string))); + } + }; + wil::networking::socket_address v4_address; v4_address.set_address_any(AF_INET); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == 0); - REQUIRE(v4_address.get_address_type() == NlatUnspecified); + REQUIRE(v4_address.address_type() == NlatUnspecified); REQUIRE(!v4_address.is_address_linklocal()); REQUIRE(!v4_address.is_address_loopback()); - REQUIRE(NlatUnspecified == v4_address.get_address_type()); REQUIRE(v4_address == test_v4_any_addr); + VerifyV4AnyAddress(v4_address, false); v4_address.set_port(TestPort); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == TestPort); - REQUIRE(v4_address.get_address_type() == NlatUnspecified); + REQUIRE(v4_address.address_type() == NlatUnspecified); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(!v4_address.is_address_loopback()); REQUIRE(v4_address == test_v4_any_addr_with_port); + VerifyV4AnyAddress(v4_address, true); // verify changing families v4_address.set_address_any(AF_INET6); REQUIRE(v4_address.family() == AF_INET6); REQUIRE(v4_address.port() == TestPort); - REQUIRE(v4_address.get_address_type() == NlatUnspecified); + REQUIRE(v4_address.address_type() == NlatUnspecified); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(!v4_address.is_address_loopback()); REQUIRE(v4_address == test_v6_any_addr_with_port); + VerifyV6AnyAddress(v4_address, true); wil::networking::socket_address v6_address; v6_address.set_address_any(AF_INET6); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == 0); - REQUIRE(v6_address.get_address_type() == NlatUnspecified); + REQUIRE(v6_address.address_type() == NlatUnspecified); REQUIRE(!v6_address.is_address_linklocal()); REQUIRE(!v6_address.is_address_loopback()); - REQUIRE(NlatUnspecified == v6_address.get_address_type()); REQUIRE(v6_address == test_v6_any_addr); + VerifyV6AnyAddress(v6_address, false); v6_address.set_port(TestPort); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == TestPort); - REQUIRE(v6_address.get_address_type() == NlatUnspecified); + REQUIRE(v6_address.address_type() == NlatUnspecified); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(!v6_address.is_address_loopback()); REQUIRE(v6_address == test_v6_any_addr_with_port); + VerifyV6AnyAddress(v6_address, true); // verify changing families v6_address.set_address_any(AF_INET); REQUIRE(v6_address.family() == AF_INET); REQUIRE(v6_address.port() == TestPort); - REQUIRE(v6_address.get_address_type() == NlatUnspecified); + REQUIRE(v6_address.address_type() == NlatUnspecified); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(!v6_address.is_address_loopback()); REQUIRE(v6_address == test_v4_any_addr_with_port); + VerifyV4AnyAddress(v6_address, true); wil::networking::socket_address defaulted_v4_address{AF_INET}; defaulted_v4_address.set_address_any(); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == 0); - REQUIRE(defaulted_v4_address.get_address_type() == NlatUnspecified); + REQUIRE(defaulted_v4_address.address_type() == NlatUnspecified); REQUIRE(!defaulted_v4_address.is_address_linklocal()); REQUIRE(!defaulted_v4_address.is_address_loopback()); - REQUIRE(NlatUnspecified == defaulted_v4_address.get_address_type()); REQUIRE(defaulted_v4_address == test_v4_any_addr); + VerifyV4AnyAddress(defaulted_v4_address, false); defaulted_v4_address.set_port(TestPort); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == TestPort); - REQUIRE(defaulted_v4_address.get_address_type() == NlatUnspecified); + REQUIRE(defaulted_v4_address.address_type() == NlatUnspecified); + REQUIRE(!defaulted_v4_address.is_address_linklocal()); + REQUIRE(!defaulted_v4_address.is_address_loopback()); REQUIRE(defaulted_v4_address == test_v4_any_addr_with_port); + VerifyV4AnyAddress(defaulted_v4_address, true); - wil::networking::socket_address defaulted_v6_address; - defaulted_v6_address.set_address_any(AF_INET6); + wil::networking::socket_address defaulted_v6_address{AF_INET6}; + defaulted_v6_address.set_address_any(); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == 0); - REQUIRE(defaulted_v6_address.get_address_type() == NlatUnspecified); + REQUIRE(defaulted_v6_address.address_type() == NlatUnspecified); REQUIRE(!defaulted_v6_address.is_address_linklocal()); REQUIRE(!defaulted_v6_address.is_address_loopback()); - REQUIRE(NlatUnspecified == defaulted_v6_address.get_address_type()); REQUIRE(defaulted_v6_address == test_v6_any_addr); + VerifyV6AnyAddress(defaulted_v6_address, false); defaulted_v6_address.set_port(TestPort); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == TestPort); - REQUIRE(defaulted_v6_address.get_address_type() == NlatUnspecified); + REQUIRE(defaulted_v6_address.address_type() == NlatUnspecified); + REQUIRE(!defaulted_v6_address.is_address_linklocal()); + REQUIRE(!defaulted_v6_address.is_address_loopback()); REQUIRE(defaulted_v6_address == test_v6_any_addr_with_port); + VerifyV6AnyAddress(defaulted_v6_address, true); + } + + SECTION("verify set_address_loopback") + { + const auto VerifyV4LoopbackAddress = [&](const wil::networking::socket_address& v4_address, bool with_port) { + wil::networking::socket_address_wstring loopback_address_test_string{}; + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(loopback_address_test_string))); + REQUIRE(0 == memcmp(Test_loopback_in_addr_string, loopback_address_test_string, sizeof(Test_loopback_in_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in_addr_string == v4_address.write_address()); +#endif + + ZeroMemory(loopback_address_test_string, sizeof(loopback_address_test_string)); + + REQUIRE(SUCCEEDED(v4_address.write_complete_address_nothrow(loopback_address_test_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_loopback_in_addr_string_with_port, loopback_address_test_string, sizeof(Test_loopback_in_addr_string_with_port))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in_addr_string_with_port == v4_address.write_complete_address()); +#endif + } + else + { + REQUIRE(0 == memcmp(Test_loopback_in_addr_string, loopback_address_test_string, sizeof(Test_loopback_in_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in_addr_string == v4_address.write_complete_address()); +#endif + } + }; + + const auto VerifyV6LoopbackAddress = [&](const wil::networking::socket_address& v6_address, bool with_port) { + wil::networking::socket_address_wstring loopback_address_test_string{}; + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(loopback_address_test_string))); + REQUIRE(0 == memcmp(Test_loopback_in6_addr_string, loopback_address_test_string, sizeof(Test_loopback_in6_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in6_addr_string == v6_address.write_address()); +#endif + + ZeroMemory(loopback_address_test_string, sizeof(loopback_address_test_string)); + + REQUIRE(SUCCEEDED(v6_address.write_complete_address_nothrow(loopback_address_test_string))); + if (with_port) + { + REQUIRE(0 == memcmp(Test_loopback_in6_addr_string_with_port, loopback_address_test_string, sizeof(Test_loopback_in6_addr_string_with_port))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in6_addr_string_with_port == v6_address.write_complete_address()); +#endif + } + else + { + REQUIRE(0 == memcmp(Test_loopback_in6_addr_string, loopback_address_test_string, sizeof(Test_loopback_in6_addr_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_loopback_in6_addr_string == v6_address.write_complete_address()); +#endif + } + }; + + wil::networking::socket_address v4_address; + v4_address.set_address_loopback(AF_INET); + REQUIRE(v4_address.family() == AF_INET); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(v4_address == test_v4_loopback_addr); + VerifyV4LoopbackAddress(v4_address, false); + + v4_address.set_port(TestPort); + REQUIRE(v4_address.family() == AF_INET); + REQUIRE(v4_address.port() == TestPort); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(v4_address == test_v4_loopback_addr_with_port); + VerifyV4LoopbackAddress(v4_address, true); + + // verify changing families + v4_address.set_address_loopback(AF_INET6); + REQUIRE(v4_address.family() == AF_INET6); + REQUIRE(v4_address.port() == TestPort); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(v4_address == test_v6_loopback_addr_with_port); + VerifyV6LoopbackAddress(v4_address, true); + + wil::networking::socket_address v6_address; + v6_address.set_address_loopback(AF_INET6); + REQUIRE(v6_address.family() == AF_INET6); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(v6_address == test_v6_loopback_addr); + VerifyV6LoopbackAddress(v6_address, false); + + v6_address.set_port(TestPort); + REQUIRE(v6_address.family() == AF_INET6); + REQUIRE(v6_address.port() == TestPort); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(v6_address == test_v6_loopback_addr_with_port); + VerifyV6LoopbackAddress(v6_address, true); + + // verify changing families + v6_address.set_address_loopback(AF_INET); + REQUIRE(v6_address.family() == AF_INET); + REQUIRE(v6_address.port() == TestPort); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(v6_address == test_v4_loopback_addr_with_port); + VerifyV4LoopbackAddress(v6_address, true); + + wil::networking::socket_address defaulted_v4_address{AF_INET}; + defaulted_v4_address.set_address_loopback(); + REQUIRE(defaulted_v4_address.family() == AF_INET); + REQUIRE(defaulted_v4_address.port() == 0); + REQUIRE(defaulted_v4_address.address_type() == NlatUnicast); + REQUIRE(!defaulted_v4_address.is_address_linklocal()); + REQUIRE(defaulted_v4_address.is_address_loopback()); + REQUIRE(defaulted_v4_address == test_v4_loopback_addr); + VerifyV4LoopbackAddress(defaulted_v4_address, false); + + defaulted_v4_address.set_port(TestPort); + REQUIRE(defaulted_v4_address.family() == AF_INET); + REQUIRE(defaulted_v4_address.port() == TestPort); + REQUIRE(defaulted_v4_address.address_type() == NlatUnicast); + REQUIRE(!defaulted_v4_address.is_address_linklocal()); + REQUIRE(defaulted_v4_address.is_address_loopback()); + REQUIRE(defaulted_v4_address == test_v4_loopback_addr_with_port); + VerifyV4LoopbackAddress(defaulted_v4_address, true); + + wil::networking::socket_address defaulted_v6_address{AF_INET6}; + defaulted_v6_address.set_address_loopback(); + REQUIRE(defaulted_v6_address.family() == AF_INET6); + REQUIRE(defaulted_v6_address.port() == 0); + REQUIRE(defaulted_v6_address.address_type() == NlatUnicast); + REQUIRE(!defaulted_v6_address.is_address_linklocal()); + REQUIRE(defaulted_v6_address.is_address_loopback()); + REQUIRE(defaulted_v6_address == test_v6_loopback_addr); + VerifyV6LoopbackAddress(defaulted_v6_address, false); + + defaulted_v6_address.set_port(TestPort); + REQUIRE(defaulted_v6_address.family() == AF_INET6); + REQUIRE(defaulted_v6_address.port() == TestPort); + REQUIRE(defaulted_v6_address.address_type() == NlatUnicast); + REQUIRE(!defaulted_v6_address.is_address_linklocal()); + REQUIRE(defaulted_v6_address.is_address_loopback()); + REQUIRE(defaulted_v6_address == test_v6_loopback_addr_with_port); + VerifyV6LoopbackAddress(defaulted_v6_address, true); + } + + SECTION("verify v4 set_sockaddr_nothrow") + { + wil::networking::socket_address v4_address; + v4_address.set_address_loopback(AF_INET); + v4_address.set_port(TestPort); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(TestPort == v4_address.port()); + + REQUIRE(SUCCEEDED(v4_address.set_sockaddr_nothrow(Test_in_addr_string))); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + wil::networking::socket_address_wstring v4_address_string{}; + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_string))); + REQUIRE(0 == memcmp(Test_in_addr_string, v4_address_string, sizeof(Test_in_addr_string))); + + REQUIRE(SUCCEEDED(v4_address.set_sockaddr_nothrow(Test_in_addr_string2))); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr2)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_string))); + REQUIRE(0 == memcmp(Test_in_addr_string2, v4_address_string, sizeof(Test_in_addr_string2))); + + REQUIRE(SUCCEEDED(v4_address.set_sockaddr_nothrow(Test_in_addr_char_string))); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + wil::networking::socket_address_string v4_address_char_string{}; + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_char_string))); + REQUIRE(0 == memcmp(Test_in_addr_char_string, v4_address_char_string, sizeof(Test_in_addr_char_string))); + + REQUIRE(SUCCEEDED(v4_address.set_sockaddr_nothrow(Test_in_addr_char_string2))); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr2)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_char_string))); + REQUIRE(0 == memcmp(Test_in_addr_char_string2, v4_address_char_string, sizeof(Test_in_addr_char_string2))); + + // set_sockaddr via a SOCKET bound to an address + ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + ::wil::networking::socket_address test_address; + test_address.set_address_loopback(AF_INET); + test_address.set_port(TestPort); + + int gle = 0; + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + v4_address.reset(AF_UNSPEC); + REQUIRE(v4_address.address_type() == NlatUnspecified); + REQUIRE(!v4_address.is_address_loopback()); + REQUIRE(0 == v4_address.port()); + + REQUIRE(SUCCEEDED(v4_address.set_sockaddr_nothrow(test_socket.get()))); + REQUIRE(AF_INET == v4_address.family()); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(TestPort == v4_address.port()); + + REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_string))); + REQUIRE(0 == memcmp(Test_loopback_in_addr_string, v4_address_string, sizeof(Test_loopback_in_addr_string))); + } + + SECTION("verify v4 set_sockaddr throwing version") + { +#ifdef WIL_ENABLE_EXCEPTIONS + wil::networking::socket_address v4_address; + v4_address.set_address_loopback(AF_INET); + v4_address.set_port(TestPort); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(TestPort == v4_address.port()); + + v4_address.set_sockaddr(Test_in_addr_string); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + std::wstring v4_address_string; + v4_address_string = v4_address.write_address(); + REQUIRE(v4_address_string == Test_in_addr_string); + + v4_address.set_sockaddr(Test_in_addr_string2); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr2)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + v4_address_string = v4_address.write_address(); + REQUIRE(v4_address_string == Test_in_addr_string2); + + v4_address.set_sockaddr(Test_in_addr_char_string); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + v4_address_string = v4_address.write_address(); + REQUIRE(v4_address_string == Test_in_addr_string); + + v4_address.set_sockaddr(Test_in_addr_char_string2); + REQUIRE(equals(*v4_address.in_addr(), Test_in_addr2)); + REQUIRE(v4_address.port() == 0); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(!v4_address.is_address_loopback()); + + v4_address_string = v4_address.write_address(); + REQUIRE(v4_address_string == Test_in_addr_string2); + + // set_sockaddr via a SOCKET bound to an address + ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + ::wil::networking::socket_address test_address; + test_address.set_address_loopback(AF_INET); + test_address.set_port(TestPort); + + int gle = 0; + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + v4_address.reset(AF_UNSPEC); + REQUIRE(v4_address.address_type() == NlatUnspecified); + REQUIRE(!v4_address.is_address_loopback()); + REQUIRE(0 == v4_address.port()); + + v4_address.set_sockaddr(test_socket.get()); + REQUIRE(AF_INET == v4_address.family()); + REQUIRE(v4_address.address_type() == NlatUnicast); + REQUIRE(v4_address.is_address_loopback()); + REQUIRE(TestPort == v4_address.port()); + + v4_address_string = v4_address.write_address(); + REQUIRE(v4_address_string == Test_loopback_in_addr_string); +#endif + } + + SECTION("verify v6 set_sockaddr_nothrow") + { + wil::networking::socket_address v6_address; + v6_address.set_address_loopback(AF_INET6); + v6_address.set_port(TestPort); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(TestPort == v6_address.port()); + + REQUIRE(SUCCEEDED(v6_address.set_sockaddr_nothrow(Test_in6_addr_string))); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + wil::networking::socket_address_wstring v6_address_string{}; + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_string))); + REQUIRE(0 == memcmp(Test_in6_addr_string, v6_address_string, sizeof(Test_in6_addr_string))); + + REQUIRE(SUCCEEDED(v6_address.set_sockaddr_nothrow(Test_in6_addr_string2))); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr2)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_string))); + REQUIRE(0 == memcmp(Test_in6_addr_string2, v6_address_string, sizeof(Test_in6_addr_string2))); + + REQUIRE(SUCCEEDED(v6_address.set_sockaddr_nothrow(Test_in6_addr_char_string))); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + wil::networking::socket_address_string v6_address_char_string{}; + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_char_string))); + REQUIRE(0 == memcmp(Test_in6_addr_char_string, v6_address_char_string, sizeof(Test_in6_addr_char_string))); + + REQUIRE(SUCCEEDED(v6_address.set_sockaddr_nothrow(Test_in6_addr_char_string2))); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr2)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_char_string))); + REQUIRE(0 == memcmp(Test_in6_addr_char_string2, v6_address_char_string, sizeof(Test_in6_addr_char_string2))); + + // set_sockaddr via a SOCKET bound to an address + ::wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + ::wil::networking::socket_address test_address; + test_address.set_address_loopback(AF_INET6); + test_address.set_port(TestPort); + + int gle = 0; + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + v6_address.reset(AF_UNSPEC); + REQUIRE(v6_address.address_type() == NlatUnspecified); + REQUIRE(!v6_address.is_address_loopback()); + REQUIRE(0 == v6_address.port()); + + REQUIRE(SUCCEEDED(v6_address.set_sockaddr_nothrow(test_socket.get()))); + REQUIRE(AF_INET6 == v6_address.family()); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(TestPort == v6_address.port()); + + REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_string))); + REQUIRE(0 == memcmp(Test_loopback_in6_addr_string, v6_address_string, sizeof(Test_loopback_in6_addr_string))); + } + + SECTION("verify v6 set_sockaddr throwing version") + { +#ifdef WIL_ENABLE_EXCEPTIONS + wil::networking::socket_address v6_address; + v6_address.set_address_loopback(AF_INET6); + v6_address.set_port(TestPort); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(TestPort == v6_address.port()); + + v6_address.set_sockaddr(Test_in6_addr_string); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + std::wstring v6_address_string; + v6_address_string = v6_address.write_address(); + REQUIRE(v6_address_string == Test_in6_addr_string); + + v6_address.set_sockaddr(Test_in6_addr_string2); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr2)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + v6_address_string = v6_address.write_address(); + REQUIRE(v6_address_string == Test_in6_addr_string2); + + v6_address.set_sockaddr(Test_in6_addr_char_string); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + v6_address_string = v6_address.write_address(); + REQUIRE(v6_address_string == Test_in6_addr_string); + + v6_address.set_sockaddr(Test_in6_addr_char_string2); + REQUIRE(equals(*v6_address.in6_addr(), Test_in6_addr2)); + REQUIRE(v6_address.port() == 0); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(!v6_address.is_address_loopback()); + + v6_address_string = v6_address.write_address(); + REQUIRE(v6_address_string == Test_in6_addr_string2); + + // set_sockaddr via a SOCKET bound to an address + ::wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + ::wil::networking::socket_address test_address; + test_address.set_address_loopback(AF_INET6); + test_address.set_port(TestPort); + + int gle = 0; + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + v6_address.reset(AF_UNSPEC); + REQUIRE(v6_address.address_type() == NlatUnspecified); + REQUIRE(!v6_address.is_address_loopback()); + REQUIRE(0 == v6_address.port()); + + v6_address.set_sockaddr(test_socket.get()); + REQUIRE(AF_INET6 == v6_address.family()); + REQUIRE(v6_address.address_type() == NlatUnicast); + REQUIRE(v6_address.is_address_loopback()); + REQUIRE(TestPort == v6_address.port()); + + v6_address_string = v6_address.write_address(); + REQUIRE(v6_address_string == Test_loopback_in6_addr_string); +#endif + } + + SECTION("verify additional set_* properties") + { + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + + v4_addr.set_port(TestPort + 1); + // should be stored in network-byte-order + REQUIRE(v4_addr.port() == TestPort + 1); + REQUIRE(v4_addr.sockaddr_in()->sin_port == htons(TestPort + 1)); + + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw values are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(123456); + v6_test_sockaddr.sin6_scope_id = htonl(234567); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + ::wil::networking::socket_address v6_addr{&v6_test_sockaddr}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == 123456); + REQUIRE(v6_addr.scope_id() == 234567); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + + v6_addr.set_flow_info(345678); + // should be stored in network-byte-order + REQUIRE(v6_addr.flow_info() == 345678); + REQUIRE(v6_addr.sockaddr_in6()->sin6_flowinfo == htonl(345678)); + + v6_addr.set_scope_id(456789); + // should be stored in network-byte-order + REQUIRE(v6_addr.scope_id() == 456789); + REQUIRE(v6_addr.sockaddr_in6()->sin6_scope_id == htonl(456789)); + } + + SECTION("verify swap") + { + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); + + ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + REQUIRE(v4_addr.family() == AF_INET); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); +#endif + + SOCKADDR_IN6 v6_test_sockaddr{}; + v6_test_sockaddr.sin6_family = AF_INET6; + // these raw values are in network byte order + v6_test_sockaddr.sin6_port = htons(TestPort); + v6_test_sockaddr.sin6_flowinfo = htonl(123456); + v6_test_sockaddr.sin6_scope_id = htonl(234567); + memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); + + ::wil::networking::socket_address v6_addr{&v6_test_sockaddr}; + REQUIRE(v6_addr.family() == AF_INET6); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); + REQUIRE(v6_addr.flow_info() == 123456); + REQUIRE(v6_addr.scope_id() == 234567); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); +#endif + + // swap v4 and v6 + wil::networking::swap(v4_addr, v6_addr); + + // verify each has the others' properties + REQUIRE(v6_addr.family() == AF_INET); + REQUIRE(v6_addr.address_type() == NlatUnicast); + REQUIRE(v6_addr.is_address_linklocal()); + REQUIRE(!v6_addr.is_address_loopback()); + REQUIRE(v6_addr.port() == TestPort); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in_addr_string == v6_addr.write_address()); +#endif + + REQUIRE(v4_addr.family() == AF_INET6); + REQUIRE(v4_addr.address_type() == NlatUnicast); + REQUIRE(v4_addr.is_address_linklocal()); + REQUIRE(!v4_addr.is_address_loopback()); + REQUIRE(v4_addr.port() == TestPort); + REQUIRE(v4_addr.flow_info() == 123456); + REQUIRE(v4_addr.scope_id() == 234567); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(Test_linklocal_in6_addr_string == v4_addr.write_address()); +#endif + } + + SECTION("verify map_dual_mode_4to6") + { + constexpr wchar_t dual_mode_string[] = L"::ffff:1.1.1.1"; + + SOCKADDR_IN v4_test_sockaddr{}; + v4_test_sockaddr.sin_family = AF_INET; + v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order + memcpy(&v4_test_sockaddr.sin_addr, &Test_in_addr, sizeof(in_addr)); + + ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + + ::wil::networking::socket_address mapped_addr = ::wil::networking::map_dual_mode_4to6(v4_addr); + REQUIRE(mapped_addr.family() == AF_INET6); + REQUIRE(IN6_IS_ADDR_V4MAPPED(mapped_addr.in6_addr())); +#ifdef WIL_ENABLE_EXCEPTIONS + REQUIRE(dual_mode_string == mapped_addr.write_address()); +#else + (void)dual_mode_string; +#endif + } +} + +TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") +{ + InitTestAddresses(); + auto wsa_startup_tracking = wil::networking::WSAStartup_nothrow(); + REQUIRE(static_cast(wsa_startup_tracking)); + + SECTION("verify set_sockaddr socket failure path") + { + + // set_sockaddr via a SOCKET bound to an address - but this time do not call bind + ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + + ::wil::networking::socket_address test_address; + REQUIRE(FAILED(test_address.set_sockaddr_nothrow(test_socket.get()))); + +#ifdef WIL_ENABLE_EXCEPTIONS + bool exception_thrown = false; + try + { + test_address.set_sockaddr(test_socket.get()); + } + catch (const wil::ResultException& e) + { + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(WSAEINVAL)); + exception_thrown = true; + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); +#endif + } + + SECTION("verify set_sockaddr_nothrow bad-address-string failure path") + { + ::wil::networking::socket_address test_address; + REQUIRE(FAILED(test_address.set_sockaddr_nothrow(L"abcdefg"))); + REQUIRE(FAILED(test_address.set_sockaddr_nothrow("abcdefg"))); + +#ifdef WIL_ENABLE_EXCEPTIONS + bool exception_thrown = false; + try + { + test_address.set_sockaddr(L"abcdefg"); + } + catch (const wil::ResultException& e) + { + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + exception_thrown = true; + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); + + exception_thrown = false; + try + { + test_address.set_sockaddr("abcdefg"); + } + catch (const wil::ResultException& e) + { + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)); + exception_thrown = true; + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); +#endif + } + + SECTION("verify write_address_nothrow failure path") + { + ::wil::networking::socket_address test_address; + test_address.reset(AF_UNSPEC); + + ::wil::networking::socket_address_wstring wstring_address; + REQUIRE(FAILED(test_address.write_address_nothrow(wstring_address))); + REQUIRE(FAILED(test_address.write_complete_address_nothrow(wstring_address))); + +#ifdef WIL_ENABLE_EXCEPTIONS + bool exception_thrown = false; + try + { + std::wstring test_string = test_address.write_address(); + // should never get here + REQUIRE(test_string.empty()); + } + catch (const wil::ResultException& e) + { + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(WSAEAFNOSUPPORT)); + exception_thrown = true; + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); + + exception_thrown = false; + try + { + std::wstring test_string = test_address.write_complete_address(); + // should never get here + REQUIRE(test_string.empty()); + } + catch (const wil::ResultException& e) + { + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(WSAEINVAL)); + exception_thrown = true; + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); +#endif + + ::wil::networking::socket_address_string string_address; + REQUIRE(FAILED(test_address.write_address_nothrow(string_address))); + REQUIRE(FAILED(test_address.write_complete_address_nothrow(string_address))); + } + + SECTION("verify write_address_nothrow maximum string size") + { + { + ::wil::networking::socket_address test_mapped_address; + REQUIRE(SUCCEEDED(test_mapped_address.set_sockaddr_nothrow(L"0000:0000:0000:0000:0000:ffff:255.255.255.255"))); + test_mapped_address.set_port(std::numeric_limits::max()); + test_mapped_address.set_scope_id(std::numeric_limits::max()); + test_mapped_address.set_flow_info(std::numeric_limits::max()); + + ::wil::networking::socket_address_wstring test_mapped_address_string; + REQUIRE(SUCCEEDED(test_mapped_address.write_address_nothrow(test_mapped_address_string))); + REQUIRE(SUCCEEDED(test_mapped_address.write_complete_address_nothrow(test_mapped_address_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring test_mapped_address_wstring = test_mapped_address.write_complete_address(); + constexpr auto* expected_test_mapped_address_string = L"[::ffff:255.255.255.255%4294967295]:65535"; + REQUIRE(expected_test_mapped_address_string == test_mapped_address_wstring); +#endif + } + + { + ::wil::networking::socket_address test_max_v6_address; + REQUIRE(SUCCEEDED(test_max_v6_address.set_sockaddr_nothrow(L"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); + test_max_v6_address.set_port(std::numeric_limits::max()); + test_max_v6_address.set_scope_id(std::numeric_limits::max()); + test_max_v6_address.set_flow_info(std::numeric_limits::max()); + + ::wil::networking::socket_address_wstring test_max_v6_address_string; + REQUIRE(SUCCEEDED(test_max_v6_address.write_address_nothrow(test_max_v6_address_string))); + REQUIRE(SUCCEEDED(test_max_v6_address.write_complete_address_nothrow(test_max_v6_address_string))); +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring test_max_v6_address_wstring = test_max_v6_address.write_complete_address(); + constexpr auto* expected_test_max_v6_address_string = L"[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%4294967295]:65535"; + REQUIRE(expected_test_max_v6_address_string == test_max_v6_address_wstring); +#endif + } + } +} + +TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") +{ + using wil::networking::equals; + + SECTION("verify winsock_extension_function_table") + { + // verify the first 3 function pointers are calling through correctly to confirm the function table is correct + ::wil::networking::winsock_extension_function_table test_table = ::wil::networking::winsock_extension_function_table::load(); + REQUIRE(static_cast(test_table)); + REQUIRE(test_table.f.AcceptEx); + REQUIRE(test_table.f.ConnectEx); + REQUIRE(test_table.f.DisconnectEx); + REQUIRE(test_table.f.GetAcceptExSockaddrs); + REQUIRE(test_table.f.TransmitFile); + REQUIRE(test_table.f.TransmitPackets); + REQUIRE(test_table.f.WSARecvMsg); + REQUIRE(test_table.f.WSASendMsg); + + // create a listening socket and post an AcceptEx on it + ::wil::unique_socket listeningSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(listeningSocket.get() != INVALID_SOCKET); + ::wil::networking::socket_address listenAddress{AF_INET6}; + listenAddress.set_address_loopback(); + listenAddress.set_port(TestPort); + + int gle = 0; + auto bind_error = ::bind(listeningSocket.get(), listenAddress.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + gle = 0; + auto listen_error = ::listen(listeningSocket.get(), 1); + if (listen_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(listen_error == 0); + + // the buffer to supply to AcceptEx to capture the address information + static constexpr size_t singleAddressOutputBufferSize = ::wil::networking::socket_address::length + 16; + char acceptex_output_buffer[singleAddressOutputBufferSize * 2]{}; + ::wil::unique_socket acceptSocket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(acceptSocket.get() != INVALID_SOCKET); + + DWORD acceptex_bytes_received{}; + ::wil::unique_event_nothrow acceptex_overlapped_event{}; + REQUIRE(SUCCEEDED(acceptex_overlapped_event.create())); + REQUIRE(acceptex_overlapped_event.get() != nullptr); + OVERLAPPED acceptex_overlapped{}; + acceptex_overlapped.hEvent = acceptex_overlapped_event.get(); + + gle = 0; + auto acceptex_return = test_table.f.AcceptEx( + listeningSocket.get(), + acceptSocket.get(), + acceptex_output_buffer, + 0, + singleAddressOutputBufferSize, + singleAddressOutputBufferSize, + &acceptex_bytes_received, + &acceptex_overlapped); + if (!acceptex_return) + { + gle = ::WSAGetLastError(); + } + // should fail with ERROR_IO_PENDING + REQUIRE(!acceptex_return); + REQUIRE(gle == ERROR_IO_PENDING); + // ensure that if this test function fails (returns) before AcceptEx completes asynchronously + // that we wait for this async (overlapped) call to complete + const auto ensure_acceptex_overlapped_completes = ::wil::scope_exit([&] { + // close the sockets to cancel any pended IO + acceptSocket.reset(); + listeningSocket.reset(); + // now wait for our async call + acceptex_overlapped_event.wait(); + }); + + // now create a socket to connect to it + ::wil::unique_event_nothrow connectex_overlapped_event{}; + REQUIRE(SUCCEEDED(connectex_overlapped_event.create())); + REQUIRE(connectex_overlapped_event.get() != nullptr); + OVERLAPPED connectex_overlapped{}; + connectex_overlapped.hEvent = connectex_overlapped_event.get(); + + ::wil::unique_socket connectingSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + REQUIRE(connectingSocket.get() != INVALID_SOCKET); + // ConnectEx requires a bound socket + ::wil::networking::socket_address connecting_from_address{AF_INET6}; + connecting_from_address.set_address_loopback(); + connecting_from_address.set_port(0); // just an ephemeral port, ConnectEx will find a port + + gle = 0; + bind_error = ::bind(connectingSocket.get(), connecting_from_address.sockaddr(), ::wil::networking::socket_address::length); + if (bind_error != 0) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(bind_error == 0); + + gle = 0; + auto connectex_return = test_table.f.ConnectEx( + connectingSocket.get(), listenAddress.sockaddr(), ::wil::networking::socket_address::length, nullptr, 0, nullptr, &connectex_overlapped); + if (!connectex_return) + { + gle = ::WSAGetLastError(); + } + // should fail with ERROR_IO_PENDING + REQUIRE(!connectex_return); + REQUIRE(gle == ERROR_IO_PENDING); + // ensure that if this test function fails (returns) before ConnectEx completes asynchronously + // that we wait for this async (overlapped) call to complete + const auto ensure_connectex_overlapped_completes = ::wil::scope_exit([&] { + // close the socket to cancel any pended IO + connectingSocket.reset(); + // now wait for our async call + connectex_overlapped_event.wait(); + }); + + // wait for both connect and accept to complete + DWORD transfer_unused{}; + DWORD flags_unused{}; + gle = 0; + auto connectex_overlapped_result = ::WSAGetOverlappedResult( + connectingSocket.get(), + &connectex_overlapped, + &transfer_unused, + TRUE, // should wait for connect to complete + &flags_unused); + if (!connectex_overlapped_result) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(connectex_overlapped_result == TRUE); + + gle = 0; + auto acceptex_overlapped_result = ::WSAGetOverlappedResult( + acceptSocket.get(), + &acceptex_overlapped, + &transfer_unused, + TRUE, // should wait for connect to complete + &flags_unused); + if (!acceptex_overlapped_result) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(acceptex_overlapped_result == TRUE); + + // issue a DisconnectEx from the client + ::wil::unique_event_nothrow disconnectex_overlapped_event{}; + REQUIRE(SUCCEEDED(disconnectex_overlapped_event.create())); + REQUIRE(disconnectex_overlapped_event.get() != nullptr); + OVERLAPPED disconnectex_overlapped{}; + disconnectex_overlapped.hEvent = disconnectex_overlapped_event.get(); + + auto disconnectex_return = test_table.f.DisconnectEx( + connectingSocket.get(), + &disconnectex_overlapped, + 0, // not passing the reuse-socket flag + 0); + if (!disconnectex_return) + { + gle = ::WSAGetLastError(); + } + // should fail with ERROR_IO_PENDING + REQUIRE(!disconnectex_return); + REQUIRE(gle == ERROR_IO_PENDING); + + gle = 0; + auto disconnectex_overlapped_result = ::WSAGetOverlappedResult( + connectingSocket.get(), + &disconnectex_overlapped, + &transfer_unused, + TRUE, // should wait for connect to complete + &flags_unused); + if (!disconnectex_overlapped_result) + { + gle = ::WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(disconnectex_overlapped_result == TRUE); + } + + SECTION("verify rio_extension_function_table") + { + // verify 2 function pointers are calling through correctly to confirm the function table is correct + ::wil::networking::rio_extension_function_table test_table = ::wil::networking::rio_extension_function_table::load(); + REQUIRE(static_cast(test_table)); + REQUIRE(test_table.f.cbSize > 0); + REQUIRE(test_table.f.RIOReceive); + REQUIRE(test_table.f.RIOReceiveEx); + REQUIRE(test_table.f.RIOSend); + REQUIRE(test_table.f.RIOSendEx); + REQUIRE(test_table.f.RIOCloseCompletionQueue); + REQUIRE(test_table.f.RIOCreateCompletionQueue); + REQUIRE(test_table.f.RIOCreateRequestQueue); + REQUIRE(test_table.f.RIODequeueCompletion); + REQUIRE(test_table.f.RIODeregisterBuffer); + REQUIRE(test_table.f.RIONotify); + REQUIRE(test_table.f.RIORegisterBuffer); + REQUIRE(test_table.f.RIOResizeCompletionQueue); + REQUIRE(test_table.f.RIOResizeRequestQueue); + + ::wil::unique_event_nothrow rio_completion_notification_event{}; + REQUIRE(SUCCEEDED(rio_completion_notification_event.create())); + REQUIRE(rio_completion_notification_event.get() != nullptr); + + RIO_NOTIFICATION_COMPLETION rio_completion_notification{}; + rio_completion_notification.Type = RIO_EVENT_COMPLETION; + rio_completion_notification.Event.EventHandle = rio_completion_notification_event.get(); + rio_completion_notification.Event.NotifyReset = FALSE; + + int gle = 0; + const RIO_CQ rio_cq = test_table.f.RIOCreateCompletionQueue( + 10, // queue size + &rio_completion_notification); + if (rio_cq == RIO_INVALID_CQ) + { + gle = WSAGetLastError(); + } + REQUIRE(gle == 0); + REQUIRE(rio_cq != RIO_INVALID_CQ); + + test_table.f.RIOCloseCompletionQueue(rio_cq); + } +} + +TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") +{ + using wil::networking::addr_info; + using wil::networking::equals; + + const auto cleanup = wil::networking::WSAStartup_nothrow(); + InitTestAddresses(); + + SECTION("verify resolve_local_addresses") + { + addr_info test_addr = wil::networking::resolve_local_addresses_nothrow(); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::networking::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + // wprintf(L"... resolve_local_addresses_nothrow : %ws\n", address_string); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + { + test_addr = wil::networking::resolve_local_addresses(); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + const auto address_string{address.write_address()}; + // wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); + } + } +#endif + } + + SECTION("verify resolve_localhost_addresses") + { + addr_info test_addr = wil::networking::resolve_localhost_addresses_nothrow(); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + // verify operator-> + REQUIRE(test_addr.begin()->is_address_loopback()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(address.is_address_loopback()); + + switch (address.family()) + { + case AF_INET: + REQUIRE(equals(*address.in_addr(), Test_loopback_in_addr)); + break; + + case AF_INET6: + REQUIRE(equals(*address.in6_addr(), Test_loopback_in6_addr)); + break; + + default: + REQUIRE(false); + } + + wil::networking::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + // wprintf(L"... resolve_localhost_addresses_nothrow : %ws\n", address_string); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + { + test_addr = wil::networking::resolve_localhost_addresses(); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + // verify operator-> + REQUIRE(test_addr.begin()->is_address_loopback()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(address.is_address_loopback()); + + switch (address.family()) + { + case AF_INET: + REQUIRE(equals(*address.in_addr(), Test_loopback_in_addr)); + break; + + case AF_INET6: + REQUIRE(equals(*address.in6_addr(), Test_loopback_in6_addr)); + break; + + default: + REQUIRE(false); + } + + const auto address_string{address.write_address()}; + // wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); + } + } +#endif + } + + SECTION("verify resolve_name") + { + addr_info test_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::networking::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + // wprintf(L"... resolve_name_nothrow(..localmachine) : %ws\n", address_string); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + { + test_addr = wil::networking::resolve_name(L"..localmachine"); + REQUIRE(test_addr.get_last_error() == 0); + REQUIRE(test_addr.begin() != test_addr.end()); + + for (const auto& address : test_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + const auto address_string{address.write_address()}; + // wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); + } + } +#endif + } + + SECTION("verify const addr_info iterators") + { + const addr_info test_addr = wil::networking::resolve_name_nothrow(L"localhost"); + REQUIRE(test_addr.begin() != test_addr.end()); + + const addr_info::iterator test_iterator = test_addr.begin(); + REQUIRE(test_iterator->is_address_loopback()); + + const auto& test_address_reference = *test_iterator; + REQUIRE(test_address_reference.is_address_loopback()); + } + + SECTION("verify addr_info iterator increment") + { + const addr_info initial_addr = wil::networking::resolve_name_nothrow(L"localhost"); + REQUIRE(initial_addr.begin() != initial_addr.end()); + + auto total_count = 0; + for (auto it = initial_addr.begin(); it != initial_addr.end(); ++it) + { + ++total_count; + } + + addr_info test_addr = wil::networking::resolve_name_nothrow(L"localhost"); + REQUIRE(initial_addr.begin() != initial_addr.end()); + + addr_info::iterator test_iterator = test_addr.begin(); + test_iterator += total_count; + REQUIRE(test_iterator == test_addr.end()); + } + + SECTION("verify addr_info iterator move behavior") + { + addr_info moved_from_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + REQUIRE(moved_from_addr.get_last_error() == 0); + REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); + + addr_info moved_to_addr = std::move(moved_from_addr); + // moved_from_addr should be end() now + REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); + + REQUIRE(moved_to_addr.get_last_error() == 0); + REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); + + for (const auto& address : moved_to_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::networking::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + // wprintf(L"... moved resolve_name_nothrow(..localmachine) : %ws\n", address_string); + } + } + + SECTION("verify addr_info iterator move assignment behavior") + { + addr_info moved_from_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + REQUIRE(moved_from_addr.get_last_error() == 0); + REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); + + addr_info moved_to_addr{wil::networking::resolve_local_addresses_nothrow()}; + moved_to_addr = std::move(moved_from_addr); + + // moved_from_addr should be end() now + REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); + + REQUIRE(moved_to_addr.get_last_error() == 0); + REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); + + // move to self + moved_to_addr = std::move(moved_to_addr); + REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); + + for (const auto& address : moved_to_addr) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::networking::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + } + } + + SECTION("verify addr_info resolve_name failure") + { + addr_info test_addr = wil::networking::resolve_name_nothrow(L"...xyz.xyz..."); + REQUIRE(test_addr.get_last_error() == WSAHOST_NOT_FOUND); + REQUIRE(test_addr.begin() == test_addr.end()); + +#ifdef WIL_ENABLE_EXCEPTIONS + bool exception_thrown = false; + try + { + test_addr = wil::networking::resolve_name(L"...xyz.xyz..."); + } + catch (const wil::ResultException& e) + { + exception_thrown = true; + REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND)); + } + catch (...) + { + REQUIRE(false); + } + REQUIRE(exception_thrown); +#endif } } \ No newline at end of file From f513f58d8f90a9b1c5e4d9019d4bfbd354c70db6 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Tue, 31 Dec 2024 20:14:17 -0800 Subject: [PATCH 11/22] cleaning up some comments. --- include/wil/networking.h | 82 ++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/include/wil/networking.h b/include/wil/networking.h index 9b9425c4..f775b9f3 100644 --- a/include/wil/networking.h +++ b/include/wil/networking.h @@ -119,10 +119,11 @@ namespace networking // (getting a WSA reference count should be no-fail once the first reference it taken) // // IF the target could not get a WSA reference count - // OR + // OR // IF we couldn't get our own WSA reference count - // (this should never happen the caller has a reference, but we failed to get a WSA reference) - // THEN this object cannot carry forward any function pointers - it must show successfully_loaded == false + // (this should never happen the caller has a reference, but we failed to get a WSA reference) + // THEN + // this object cannot carry forward any function pointers - it must show successfully loaded == false winsock_extension_function_table(const winsock_extension_function_table& rhs) WI_NOEXCEPT : wsa_reference_count{WSAStartup_nothrow()} { @@ -179,10 +180,11 @@ namespace networking // (getting a WSA reference count should be no-fail once the first reference it taken) // // IF the target could not get a WSA reference count - // OR + // OR // IF we couldn't get our own WSA reference count - // (this should never happen the caller has a reference, but we failed to get a WSA reference) - // THEN this object cannot carry forward any function pointers - it must show successfully_loaded == false + // (this should never happen the caller has a reference, but we failed to get a WSA reference) + // THEN + // this object cannot carry forward any function pointers - it must show successfully loaded == false rio_extension_function_table(const rio_extension_function_table& rhs) WI_NOEXCEPT : wsa_reference_count{WSAStartup_nothrow()} { @@ -230,40 +232,40 @@ namespace networking }; // - // encapsulates working with the sockaddr datatype - // - // sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) - // 'derived' structures are cast back to sockaddr* (so the initial struct members must be aligned) - // - // this data type was built to be 'extensible' so new network types could create their own address structures - // - appending fields to the initial fields of the sockaddr - // - // note that the address and port fields of TCPIP sockaddr* types were designed to be encoded in network-byte order - // - hence the common use of "host-to-network" and "network-to-host" APIs, e.g. htons(), htonl(), ntohs(), ntohl() - // - // Socket APIs that accept a socket address will accept 2 fields: - // - the sockaddr* (the address of the derived sockaddr type, cast down to a sockaddr*) - // - the length of the 'derived' socket address structure referenced by the sockaddr* - // - // Commonly used sockaddr* types that are using with TCPIP networking: - // - // sockaddr_storage / SOCKADDR_STORAGE - // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address - // sockaddr_in / SOCKADDR_IN - // - a sockaddr* derived type designed to contain an IPv4 address and port number - // sockaddr_in6 / SOCKADDR_IN6 - // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info - // SOCKADDR_INET - // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address - // in_addr / IN_ADDR - // - the raw address portion of a sockaddr_in - // in6_addr / IN6_ADDR - // - the raw address portion of a sockaddr_in6 - // - // SOCKET_ADDRESS - // - not a derived sockaddr* type - // - a structure containing both a sockaddr* and its length fields, returned from some networking functions - // + //! encapsulates working with the sockaddr datatype + //! + //! sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) + //! 'derived' structures are cast back to sockaddr* (so the initial struct members must be aligned) + //! + //! this data type was built to be 'extensible' so new network types could create their own address structures + //! - appending fields to the initial fields of the sockaddr + //! + //! note that the address and port fields of TCPIP sockaddr* types were designed to be encoded in network-byte order + //! - hence the common use of "host-to-network" and "network-to-host" APIs, e.g. htons(), htonl(), ntohs(), ntohl() + //! + //! Socket APIs that accept a socket address will accept 2 fields: + //! - the sockaddr* (the address of the derived sockaddr type, cast down to a sockaddr*) + //! - the length of the 'derived' socket address structure referenced by the sockaddr* + //! + //! Commonly used sockaddr* types that are using with TCPIP networking: + //! + //! sockaddr_storage / SOCKADDR_STORAGE + //! - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address + //! sockaddr_in / SOCKADDR_IN + //! - a sockaddr* derived type designed to contain an IPv4 address and port number + //! sockaddr_in6 / SOCKADDR_IN6 + //! - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info + //! SOCKADDR_INET + //! - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address + //! in_addr / IN_ADDR + //! - the raw address portion of a sockaddr_in + //! in6_addr / IN6_ADDR + //! - the raw address portion of a sockaddr_in6 + //! + //! SOCKET_ADDRESS + //! - not a derived sockaddr* type + //! - a structure containing both a sockaddr* and its length fields, returned from some networking functions + //! [[nodiscard]] inline bool equals(const in_addr& lhs, const in_addr& rhs) WI_NOEXCEPT { From 22f371e95549520097d92dacb6e9fe6137c486a7 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 11 Jan 2025 17:59:01 -0800 Subject: [PATCH 12/22] Addressing feedback --- include/wil/{networking.h => network.h} | 326 ++++++++++++------------ tests/NetworkingTests.cpp | 313 ++++++++++++----------- 2 files changed, 334 insertions(+), 305 deletions(-) rename include/wil/{networking.h => network.h} (76%) diff --git a/include/wil/networking.h b/include/wil/network.h similarity index 76% rename from include/wil/networking.h rename to include/wil/network.h index f775b9f3..5d65a7f6 100644 --- a/include/wil/networking.h +++ b/include/wil/network.h @@ -11,8 +11,8 @@ //! @file //! Helpers for using BSD sockets and Winsock functions and structures. //! Does not require the use of the STL or C++ exceptions (see _nothrow functions) -#ifndef __WIL_NETWORKING_INCLUDED -#define __WIL_NETWORKING_INCLUDED +#ifndef __WIL_NETWORK_INCLUDED +#define __WIL_NETWORK_INCLUDED #ifdef _KERNEL_MODE #error This header is not supported in kernel-mode. @@ -31,7 +31,7 @@ //! Link libs for functions referenced in this file: ws2_32.lib, ntdll.lib, and Fwpuclnt.lib (for secure socket functions) #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) -#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h +#error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h, or include winsock2.h before windows.h #endif //! Including Winsock and networking headers in the below specific sequence @@ -52,9 +52,8 @@ namespace wil { //! Functions and classes that support networking operations and structures -namespace networking +namespace network { - class socket_address; //! A type that calls WSACleanup on destruction (or reset()). //! WSAStartup must be called for the lifetime of all Winsock APIs (synchronous and asynchronous) //! WSACleanup will unload the full Winsock catalog - all the libraries - with the final reference @@ -62,13 +61,13 @@ namespace networking using unique_wsacleanup_call = ::wil::unique_call; //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed - WI_NODISCARD inline unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT + WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT { WSADATA unused_data{}; - const auto error = ::WSAStartup(WINSOCK_VERSION, &unused_data); + const auto error{::WSAStartup(WINSOCK_VERSION, &unused_data)}; LOG_IF_WIN32_ERROR(error); - unique_wsacleanup_call return_cleanup{}; + ::wil::network::unique_wsacleanup_call return_cleanup{}; if (error != 0) { // internally set m_call to false @@ -80,7 +79,7 @@ namespace networking } //! Calls WSAStartup and fail-fasts on error; returns an RAII object that reverts - WI_NODISCARD inline unique_wsacleanup_call WSAStartup_failfast() WI_NOEXCEPT + WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup_failfast() WI_NOEXCEPT { WSADATA unused_data{}; FAIL_FAST_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); @@ -89,7 +88,7 @@ namespace networking #if defined(WIL_ENABLE_EXCEPTIONS) //! Calls WSAStartup and throws on error; returns an RAII object that reverts - WI_NODISCARD inline unique_wsacleanup_call WSAStartup() + WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup() { WSADATA unused_data{}; THROW_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); @@ -157,7 +156,7 @@ namespace networking return f.AcceptEx != nullptr; } - WINSOCK_EXTENSION_FUNCTION_TABLE f{}; + ::wil::network::WINSOCK_EXTENSION_FUNCTION_TABLE f{}; private: // constructed via load() @@ -167,7 +166,7 @@ namespace networking } // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const unique_wsacleanup_call wsa_reference_count; + const ::wil::network::unique_wsacleanup_call wsa_reference_count; }; struct rio_extension_function_table @@ -218,7 +217,7 @@ namespace networking return f.RIOReceive != nullptr; } - RIO_EXTENSION_FUNCTION_TABLE f{}; + ::RIO_EXTENSION_FUNCTION_TABLE f{}; private: // constructed via load() @@ -228,7 +227,7 @@ namespace networking } // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const unique_wsacleanup_call wsa_reference_count; + const ::wil::network::unique_wsacleanup_call wsa_reference_count; }; // @@ -354,8 +353,8 @@ namespace networking // write_address prints the IP address portion, not the scope id or port #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - [[nodiscard]] std::wstring write_address() const; - [[nodiscard]] std::wstring write_complete_address() const; + [[nodiscard]] ::std::wstring write_address() const; + [[nodiscard]] ::std::wstring write_complete_address() const; #endif HRESULT write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; @@ -389,7 +388,7 @@ namespace networking [[nodiscard]] const IN_ADDR* in_addr() const WI_NOEXCEPT; [[nodiscard]] const IN6_ADDR* in6_addr() const WI_NOEXCEPT; - static constexpr int length{sizeof(SOCKADDR_INET)}; + static constexpr int length{sizeof(::SOCKADDR_INET)}; private: SOCKADDR_INET m_sockaddr{}; @@ -397,14 +396,14 @@ namespace networking // When using dual-mode sockets, one might need to connect to a target IPv4 address. // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address - inline socket_address map_dual_mode_4to6(const socket_address& inV4) WI_NOEXCEPT + inline ::wil::network::socket_address map_dual_mode_4to6(const ::wil::network::socket_address& inV4) WI_NOEXCEPT { constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; - socket_address outV6{&v4MappedPrefix, inV4.port()}; + ::wil::network::socket_address outV6{&v4MappedPrefix, inV4.port()}; - auto* const pIn6Addr = outV6.in6_addr(); - const auto* const pIn4Addr = inV4.in_addr(); + auto* const pIn6Addr{outV6.in6_addr()}; + const auto* const pIn4Addr{inV4.in_addr()}; pIn6Addr->u.Byte[12] = pIn4Addr->S_un.S_un_b.s_b1; pIn6Addr->u.Byte[13] = pIn4Addr->S_un.S_un_b.s_b2; pIn6Addr->u.Byte[14] = pIn4Addr->S_un.S_un_b.s_b3; @@ -416,7 +415,7 @@ namespace networking // // non-member swap // - inline void swap(socket_address& lhs, socket_address& rhs) WI_NOEXCEPT + inline void swap(::wil::network::socket_address& lhs, ::wil::network::socket_address& rhs) WI_NOEXCEPT { lhs.swap(rhs); } @@ -480,11 +479,11 @@ namespace networking #if defined(_ITERATOR_) || defined(WIL_DOXYGEN) using iterator_category = ::std::forward_iterator_tag; #endif - using value_type = socket_address; + using value_type = ::wil::network::socket_address; using difference_type = size_t; using distance_type = size_t; - using pointer = socket_address*; - using reference = socket_address&; + using pointer = ::wil::network::socket_address*; + using reference = ::wil::network::socket_address&; iterator(const ADDRINFOW* addr_info) WI_NOEXCEPT : m_addr_info(const_cast(addr_info)) @@ -502,22 +501,22 @@ namespace networking iterator(iterator&&) WI_NOEXCEPT = default; iterator& operator=(iterator&&) WI_NOEXCEPT = default; - const socket_address& operator*() const WI_NOEXCEPT + const ::wil::network::socket_address& operator*() const WI_NOEXCEPT { return m_socket_address; } - const socket_address& operator*() WI_NOEXCEPT + const ::wil::network::socket_address& operator*() WI_NOEXCEPT { return m_socket_address; } - const socket_address* operator->() const WI_NOEXCEPT + const ::wil::network::socket_address* operator->() const WI_NOEXCEPT { return &m_socket_address; } - const socket_address* operator->() WI_NOEXCEPT + const ::wil::network::socket_address* operator->() WI_NOEXCEPT { return &m_socket_address; } @@ -568,7 +567,7 @@ namespace networking private: // non-ownership of this pointer - the parent class must outlive the iterator ADDRINFOW* m_addr_info{nullptr}; - socket_address m_socket_address{}; + ::wil::network::socket_address m_socket_address{}; }; iterator begin() const WI_NOEXCEPT @@ -590,9 +589,9 @@ namespace networking //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::networking::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT + inline ::wil::network::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT { - int lastError = 0; + int lastError{0}; ADDRINFOW* addrResult{}; if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) { @@ -606,25 +605,25 @@ namespace networking //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::networking::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT + inline ::wil::network::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT { - return ::wil::networking::resolve_name_nothrow(L""); + return ::wil::network::resolve_name_nothrow(L""); } //! wil function to capture resolving the local-host addresses //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::networking::addr_info resolve_localhost_addresses_nothrow() WI_NOEXCEPT + inline ::wil::network::addr_info resolve_localhost_addresses_nothrow() WI_NOEXCEPT { - return ::wil::networking::resolve_name_nothrow(L"localhost"); + return ::wil::network::resolve_name_nothrow(L"localhost"); } #if defined(WIL_ENABLE_EXCEPTIONS) //! wil function to capture resolving a name to a set of IP addresses, throwing on error //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::networking::addr_info resolve_name(_In_ PCWSTR name) + inline ::wil::network::addr_info resolve_name(_In_ PCWSTR name) { ADDRINFOW* addrResult{}; if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) @@ -638,78 +637,78 @@ namespace networking //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::networking::addr_info resolve_local_addresses() WI_NOEXCEPT + inline ::wil::network::addr_info resolve_local_addresses() WI_NOEXCEPT { - return ::wil::networking::resolve_name(L""); + return ::wil::network::resolve_name(L""); } //! wil function to capture resolving the local-host addresses, throwing on error //! returning an RAII object containing the results //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::networking::addr_info resolve_localhost_addresses() WI_NOEXCEPT + inline ::wil::network::addr_info resolve_localhost_addresses() WI_NOEXCEPT { - return ::wil::networking::resolve_name(L"localhost"); + return ::wil::network::resolve_name(L"localhost"); } #endif // // socket_address definitions // - inline socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT { reset(family); } template - socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + ::wil::network::socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT { set_sockaddr(addr, inLength); } - inline socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT { - m_sockaddr.si_family = AF_INET; + reset(AF_INET); set_address(addr); set_port(port); } - inline socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline ::wil::network::socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT { - m_sockaddr.si_family = AF_INET6; + reset(AF_INET6); set_address(addr); set_port(port); } #if defined(WIL_ENABLE_EXCEPTIONS) - inline socket_address::socket_address(PCWSTR addr, unsigned short port) + inline ::wil::network::socket_address::socket_address(PCWSTR addr, unsigned short port) { set_sockaddr(addr); set_port(port); } #endif - inline bool socket_address::operator==(const socket_address& rhs) const WI_NOEXCEPT + inline bool ::wil::network::socket_address::operator==(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { - const auto& lhs = *this; + const auto& lhs{*this}; // Follows the same documented comparison logic as GetTcpTable2 and GetTcp6Table2 if (lhs.family() != rhs.family()) @@ -726,14 +725,14 @@ namespace networking return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) == 0; } - inline bool socket_address::operator!=(const socket_address& rhs) const WI_NOEXCEPT + inline bool ::wil::network::socket_address::operator!=(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this == rhs); } - inline bool socket_address::operator<(const socket_address& rhs) const WI_NOEXCEPT + inline bool ::wil::network::socket_address::operator<(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { - const auto& lhs = *this; + const auto& lhs{*this}; if (lhs.family() != rhs.family()) { @@ -748,7 +747,7 @@ namespace networking case AF_INET: { // compare the address first - auto comparison = ::memcmp(lhs.in_addr(), rhs.in_addr(), sizeof(IN_ADDR)); + auto comparison{::memcmp(lhs.in_addr(), rhs.in_addr(), sizeof(IN_ADDR))}; if (comparison != 0) { return comparison < 0; @@ -756,8 +755,8 @@ namespace networking // then compare the port (host-byte-order) // only resolve the ntohs() once - const auto lhs_port = lhs.port(); - const auto rhs_port = rhs.port(); + const auto lhs_port{lhs.port()}; + const auto rhs_port{rhs.port()}; if (lhs_port != rhs_port) { return lhs_port < rhs_port; @@ -769,7 +768,7 @@ namespace networking case AF_INET6: { // compare the address first - auto comparison = ::memcmp(lhs.in6_addr(), rhs.in6_addr(), sizeof(IN6_ADDR)); + auto comparison{::memcmp(lhs.in6_addr(), rhs.in6_addr(), sizeof(IN6_ADDR))}; if (comparison != 0) { return comparison < 0; @@ -777,24 +776,24 @@ namespace networking // then compare the port (host-byte-order) // only resolve the ntohs() once - const auto lhs_port = lhs.port(); - const auto rhs_port = rhs.port(); + const auto lhs_port{lhs.port()}; + const auto rhs_port{rhs.port()}; if (lhs_port != rhs_port) { return lhs_port < rhs_port; } // then compare the scope_id of the address - const auto lhs_scope_id = lhs.scope_id(); - const auto rhs_scope_id = rhs.scope_id(); + const auto lhs_scope_id{lhs.scope_id()}; + const auto rhs_scope_id{rhs.scope_id()}; if (lhs_scope_id != rhs_scope_id) { return lhs_scope_id < rhs_scope_id; } // then compare flow_info - const auto lhs_flow_info = lhs.flow_info(); - const auto rhs_flow_info = rhs.flow_info(); + const auto lhs_flow_info{lhs.flow_info()}; + const auto rhs_flow_info{rhs.flow_info()}; if (lhs_flow_info != rhs_flow_info) { return lhs_flow_info < rhs_flow_info; @@ -810,12 +809,12 @@ namespace networking } } - inline bool socket_address::operator>(const socket_address& rhs) const WI_NOEXCEPT + inline bool ::wil::network::socket_address::operator>(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this < rhs); } - inline void socket_address::swap(socket_address& addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::swap(socket_address& addr) WI_NOEXCEPT { SOCKADDR_INET tempAddr{}; ::memcpy_s(&tempAddr, sizeof(tempAddr), &addr.m_sockaddr, sizeof(addr.m_sockaddr)); @@ -823,59 +822,59 @@ namespace networking ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), &tempAddr, sizeof(tempAddr)); } - inline void socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void ::wil::network::socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT { WI_ASSERT(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); m_sockaddr.si_family = family; } template - void socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + void ::wil::network::socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT { - WI_ASSERT(static_cast(inLength) <= socket_address::length); + WI_ASSERT(static_cast(inLength) <= ::wil::network::socket_address::length); - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); if (addr) { ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, inLength); } } - inline void socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); ::memcpy_s(&m_sockaddr.Ipv4, sizeof(m_sockaddr.Ipv4), addr, sizeof(*addr)); } - inline void socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); ::memcpy_s(&m_sockaddr.Ipv6, sizeof(m_sockaddr.Ipv6), addr, sizeof(*addr)); } - inline void socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, sizeof(*addr)); } - inline void socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { FAIL_FAST_IF_MSG( - addr->lpSockaddr && addr->iSockaddrLength > socket_address::length, + addr->lpSockaddr && addr->iSockaddrLength > ::wil::network::socket_address::length, "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", addr->iSockaddrLength); - ::memset(&m_sockaddr, 0, socket_address::length); + ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); if (addr->lpSockaddr) { ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr->lpSockaddr, addr->iSockaddrLength); } } - inline bool socket_address::is_address_linklocal() const WI_NOEXCEPT + inline bool ::wil::network::socket_address::is_address_linklocal() const WI_NOEXCEPT { switch (family()) { @@ -894,7 +893,7 @@ namespace networking } } - inline bool socket_address::is_address_loopback() const WI_NOEXCEPT + inline bool ::wil::network::socket_address::is_address_loopback() const WI_NOEXCEPT { switch (family()) { @@ -913,12 +912,12 @@ namespace networking } } - inline NL_ADDRESS_TYPE socket_address::address_type() const WI_NOEXCEPT + inline NL_ADDRESS_TYPE wil::network::socket_address::address_type() const WI_NOEXCEPT { switch (family()) { case AF_UNSPEC: - return NlatUnspecified; + return ::NlatUnspecified; case AF_INET: return ::Ipv4AddressType(reinterpret_cast(in_addr())); @@ -928,11 +927,11 @@ namespace networking default: WI_ASSERT_MSG(false, "Unknown address family"); - return NlatInvalid; + return ::NlatInvalid; } } - inline void socket_address::set_port(USHORT port) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_port(USHORT port) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET || family() == AF_INET6); @@ -943,7 +942,7 @@ namespace networking m_sockaddr.Ipv4.sin_port = ::htons(port); } - inline void socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -953,7 +952,7 @@ namespace networking } } - inline void socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -963,34 +962,34 @@ namespace networking } } - inline void socket_address::set_address_any() WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address_any() WI_NOEXCEPT { set_address_any(family()); } - inline void socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT { WI_ASSERT(family == AF_INET || family == AF_INET6); // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); - const auto original_port = m_sockaddr.Ipv4.sin_port; + const auto original_port{m_sockaddr.Ipv4.sin_port}; reset(family); m_sockaddr.Ipv4.sin_port = original_port; } - inline void socket_address::set_address_loopback() WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address_loopback() WI_NOEXCEPT { set_address_loopback(family()); } - inline void socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT { // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); - const auto original_port = m_sockaddr.Ipv4.sin_port; + const auto original_port{m_sockaddr.Ipv4.sin_port}; reset(family); switch (family) { @@ -1006,49 +1005,49 @@ namespace networking m_sockaddr.Ipv4.sin_port = original_port; } - inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); - const auto original_port = m_sockaddr.Ipv4.sin_port; + const auto original_port{m_sockaddr.Ipv4.sin_port}; reset(AF_INET); m_sockaddr.Ipv4.sin_addr.s_addr = addr->s_addr; m_sockaddr.Ipv4.sin_port = original_port; } - inline void socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT + inline void ::wil::network::socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); - const auto original_port = m_sockaddr.Ipv6.sin6_port; + const auto original_port{m_sockaddr.Ipv6.sin6_port}; reset(AF_INET6); m_sockaddr.Ipv6.sin6_addr = *addr; m_sockaddr.Ipv6.sin6_port = original_port; } #if defined(WIL_ENABLE_EXCEPTIONS) - inline void socket_address::set_sockaddr(SOCKET s) + inline void ::wil::network::socket_address::set_sockaddr(SOCKET s) { THROW_IF_FAILED(set_sockaddr_nothrow(s)); } - inline void socket_address::set_sockaddr(PCWSTR address) + inline void ::wil::network::socket_address::set_sockaddr(PCWSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } - inline void socket_address::set_sockaddr(PCSTR address) + inline void ::wil::network::socket_address::set_sockaddr(PCSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } #endif - inline HRESULT socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT { reset(AF_UNSPEC); - auto nameLength = socket_address::length; - auto error = ::getsockname(s, sockaddr(), &nameLength); + auto nameLength{::wil::network::socket_address::length}; + auto error{::getsockname(s, sockaddr(), &nameLength)}; if (error != 0) { error = ::WSAGetLastError(); @@ -1057,12 +1056,12 @@ namespace networking return S_OK; } - inline HRESULT socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT { PCWSTR terminator_unused; reset(AF_INET); - constexpr BOOLEAN strict_string = TRUE; + constexpr BOOLEAN strict_string{TRUE}; if (RtlIpv4StringToAddressW(address, strict_string, &terminator_unused, in_addr()) == 0) { m_sockaddr.si_family = AF_INET; @@ -1080,12 +1079,12 @@ namespace networking return E_INVALIDARG; } - inline HRESULT socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT { PCSTR terminator_unused; reset(AF_INET); - constexpr BOOLEAN strict_string = TRUE; + constexpr BOOLEAN strict_string{TRUE}; if (RtlIpv4StringToAddressA(address, strict_string, &terminator_unused, in_addr()) == 0) { m_sockaddr.si_family = AF_INET; @@ -1104,103 +1103,108 @@ namespace networking } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline std::wstring socket_address::write_address() const + inline ::std::wstring ::wil::network::socket_address::write_address() const { - socket_address_wstring returnString{}; + ::wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); - const void* const pAddr = family() == AF_INET - ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr); + const void* const pAddr{family() == AF_INET + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; + // the last param to InetNtopW is # of characters, not bytes - const auto* error_value = ::InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN); + const auto* error_value{::InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN)}; if (error_value == nullptr) { - const auto gle = ::WSAGetLastError(); - RETURN_WIN32(gle); + RETURN_WIN32(::WSAGetLastError()); } return S_OK; } - inline HRESULT socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); - const void* const pAddr = family() == AF_INET + const void* const pAddr{family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr); + : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; + // the last param to InetNtopA is # of characters, not bytes - const auto* error_value = ::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN); + const auto* error_value{::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN)}; if (error_value == nullptr) { - const auto gle = ::WSAGetLastError(); - RETURN_WIN32(gle); + RETURN_WIN32(::WSAGetLastError()); } return S_OK; } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline std::wstring socket_address::write_complete_address() const + inline ::std::wstring ::wil::network::socket_address::write_complete_address() const { - socket_address_wstring returnString{}; + ::wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_complete_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::write_complete_address_nothrow( + socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); + // socket_address will return an empty string for AF_UNSPEC (the default family type when constructed) + // as to not fail if just default constructed + if (family() == AF_UNSPEC) + { + return S_OK; + } // addressLength == # of chars, not bytes - DWORD addressLength = INET6_ADDRSTRLEN; + DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringW( const_cast(sockaddr()), - socket_address::length, + ::wil::network::socket_address::length, nullptr, address, &addressLength) != 0) { - const auto gle = ::WSAGetLastError(); - RETURN_WIN32(gle); + RETURN_WIN32(::WSAGetLastError()); } return S_OK; } // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API #if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) - inline HRESULT socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT ::wil::network::socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); - DWORD addressLength = INET6_ADDRSTRLEN; + DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringA( const_cast(sockaddr()), - socket_address::length, + ::wil::network::socket_address::length, nullptr, address, &addressLength) != 0) { - const auto gle = ::WSAGetLastError(); - RETURN_WIN32(gle); + RETURN_WIN32(::WSAGetLastError()); } return S_OK; } #endif - inline ADDRESS_FAMILY socket_address::family() const WI_NOEXCEPT + inline ADDRESS_FAMILY::wil::network::socket_address::family() const WI_NOEXCEPT { return m_sockaddr.si_family; } - inline USHORT socket_address::port() const WI_NOEXCEPT + inline USHORT::wil::network::socket_address::port() const WI_NOEXCEPT { switch (family()) { @@ -1216,7 +1220,7 @@ namespace networking } } - inline ULONG socket_address::flow_info() const WI_NOEXCEPT + inline ULONG::wil::network::socket_address::flow_info() const WI_NOEXCEPT { switch (family()) { @@ -1232,7 +1236,7 @@ namespace networking } } - inline ULONG socket_address::scope_id() const WI_NOEXCEPT + inline ULONG::wil::network::socket_address::scope_id() const WI_NOEXCEPT { switch (family()) { @@ -1248,69 +1252,69 @@ namespace networking } } - inline SOCKADDR* socket_address::sockaddr() WI_NOEXCEPT + inline SOCKADDR* ::wil::network::socket_address::sockaddr() WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline SOCKADDR_IN* socket_address::sockaddr_in() WI_NOEXCEPT + inline SOCKADDR_IN* ::wil::network::socket_address::sockaddr_in() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline SOCKADDR_IN6* socket_address::sockaddr_in6() WI_NOEXCEPT + inline SOCKADDR_IN6* ::wil::network::socket_address::sockaddr_in6() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline SOCKADDR_INET* socket_address::sockaddr_inet() WI_NOEXCEPT + inline SOCKADDR_INET* ::wil::network::socket_address::sockaddr_inet() WI_NOEXCEPT { return &m_sockaddr; } - inline IN_ADDR* socket_address::in_addr() WI_NOEXCEPT + inline IN_ADDR* ::wil::network::socket_address::in_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline IN6_ADDR* socket_address::in6_addr() WI_NOEXCEPT + inline IN6_ADDR* ::wil::network::socket_address::in6_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } - inline const SOCKADDR* socket_address::sockaddr() const WI_NOEXCEPT + inline const SOCKADDR* ::wil::network::socket_address::sockaddr() const WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline const SOCKADDR_IN* socket_address::sockaddr_in() const WI_NOEXCEPT + inline const SOCKADDR_IN* ::wil::network::socket_address::sockaddr_in() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline const SOCKADDR_IN6* socket_address::sockaddr_in6() const WI_NOEXCEPT + inline const SOCKADDR_IN6* ::wil::network::socket_address::sockaddr_in6() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline const SOCKADDR_INET* socket_address::sockaddr_inet() const WI_NOEXCEPT + inline const SOCKADDR_INET* ::wil::network::socket_address::sockaddr_inet() const WI_NOEXCEPT { return &m_sockaddr; } - inline const IN_ADDR* socket_address::in_addr() const WI_NOEXCEPT + inline const IN_ADDR* ::wil::network::socket_address::in_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline const IN6_ADDR* socket_address::in6_addr() const WI_NOEXCEPT + inline const IN6_ADDR* ::wil::network::socket_address::in6_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; @@ -1336,10 +1340,10 @@ namespace networking } // walk WINSOCK_EXTENSION_FUNCTION_TABLE to load each function pointer - constexpr auto function_table_length = sizeof(table.f) / sizeof(void*); + constexpr auto function_table_length{sizeof(table.f) / sizeof(void*)}; static_assert(function_table_length == 8); - bool successfully_loaded = true; + bool successfully_loaded{true}; for (auto fnLoop = 0ul; successfully_loaded && fnLoop < function_table_length; ++fnLoop) { void* functionPtr{}; @@ -1422,8 +1426,7 @@ namespace networking nullptr, nullptr) != 0) { - const auto gle = ::WSAGetLastError(); - LOG_IF_WIN32_ERROR(gle); + LOG_IF_WIN32_ERROR(::WSAGetLastError()); // if any failed to be found, something is very broken // all should load, or all should fail @@ -1473,15 +1476,14 @@ namespace networking nullptr, nullptr) != 0) { - const auto gle = ::WSAGetLastError(); - LOG_IF_WIN32_ERROR(gle); + LOG_IF_WIN32_ERROR(::WSAGetLastError()); ::memset(&table.f, 0, bytes); } return table; } -} // namespace networking +} // namespace network } // namespace wil -#endif // __WIL_NETWORKING_INCLUDED \ No newline at end of file +#endif // __WIL_NETWORK_INCLUDED \ No newline at end of file diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 073e4922..e1a84b19 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -8,7 +8,7 @@ // set this to give access to all functions #define _WINSOCK_DEPRECATED_NO_WARNINGS -#include +#include #include "common.h" @@ -134,7 +134,7 @@ TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") SECTION("Verifying _nothrow") { - const auto cleanup = wil::networking::WSAStartup_nothrow(); + const auto cleanup = wil::network::WSAStartup_nothrow(); const bool succeeded = !!cleanup; REQUIRE(succeeded); const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -144,7 +144,7 @@ TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") SECTION("Verifying _failfast") { - const auto cleanup = wil::networking::WSAStartup_failfast(); + const auto cleanup = wil::network::WSAStartup_failfast(); const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); REQUIRE(socket_test != INVALID_SOCKET); ::closesocket(socket_test); @@ -153,7 +153,7 @@ TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") #ifdef WIL_ENABLE_EXCEPTIONS SECTION("Verifying throwing") { - const auto cleanup = wil::networking::WSAStartup(); + const auto cleanup = wil::network::WSAStartup(); const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); REQUIRE(socket_test != INVALID_SOCKET); ::closesocket(socket_test); @@ -163,7 +163,7 @@ TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") { - using wil::networking::socket_address; + using wil::network::socket_address; InitTestAddresses(); SECTION("socket_address(ADDRESS_FAMILY)") @@ -377,31 +377,31 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::networking::socket_address::length == sizeof(SOCKADDR_INET)); + REQUIRE(wil::network::socket_address::length == sizeof(SOCKADDR_INET)); - wil::networking::socket_address default_addr{}; + wil::network::socket_address default_addr{}; - wil::networking::socket_address test_v4_addr{&Test_in_addr}; - wil::networking::socket_address test_v4_addr2{&Test_in_addr2}; - wil::networking::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; + wil::network::socket_address test_v4_addr{&Test_in_addr}; + wil::network::socket_address test_v4_addr2{&Test_in_addr2}; + wil::network::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; - wil::networking::socket_address test_v6_addr{&Test_in6_addr}; - wil::networking::socket_address test_v6_addr2{&Test_in6_addr2}; - wil::networking::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; + wil::network::socket_address test_v6_addr{&Test_in6_addr}; + wil::network::socket_address test_v6_addr2{&Test_in6_addr2}; + wil::network::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; - wil::networking::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; - wil::networking::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; + wil::network::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; + wil::network::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; - wil::networking::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; - wil::networking::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; + wil::network::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; + wil::network::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; - wil::networking::socket_address test_v4_any_addr{&Test_any_in_addr}; - wil::networking::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; + wil::network::socket_address test_v4_any_addr{&Test_any_in_addr}; + wil::network::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; - wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; - wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + wil::network::socket_address test_v6_any_addr{&Test_any_in6_addr}; + wil::network::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; - using wil::networking::equals; + using wil::network::equals; SECTION("IPv4 in_addr properties") { @@ -692,10 +692,10 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") { - using wil::networking::equals; + using wil::network::equals; #ifdef WIL_ENABLE_EXCEPTIONS - using wil::networking::socket_address; + using wil::network::socket_address; SECTION("verify v4 address comparisons") { // equal will be considered less-than @@ -787,46 +787,46 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::networking::socket_address::length == sizeof(SOCKADDR_INET)); + REQUIRE(wil::network::socket_address::length == sizeof(SOCKADDR_INET)); - wil::networking::socket_address default_addr{}; + wil::network::socket_address default_addr{}; - wil::networking::socket_address test_v4_addr{&Test_in_addr}; - wil::networking::socket_address test_v4_addr2{&Test_in_addr2}; - wil::networking::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; + wil::network::socket_address test_v4_addr{&Test_in_addr}; + wil::network::socket_address test_v4_addr2{&Test_in_addr2}; + wil::network::socket_address test_v4_addr_with_port{&Test_in_addr, TestPort}; - wil::networking::socket_address test_v6_addr{&Test_in6_addr}; - wil::networking::socket_address test_v6_addr2{&Test_in6_addr2}; - wil::networking::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; + wil::network::socket_address test_v6_addr{&Test_in6_addr}; + wil::network::socket_address test_v6_addr2{&Test_in6_addr2}; + wil::network::socket_address test_v6_addr_with_port{&Test_in6_addr, TestPort}; - wil::networking::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; - wil::networking::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; + wil::network::socket_address test_v4_linklocal_addr{&Test_linklocal_in_addr}; + wil::network::socket_address test_v4_linklocal_addr_with_port{&Test_linklocal_in_addr, TestPort}; - wil::networking::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; - wil::networking::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; + wil::network::socket_address test_v6_linklocal_addr{&Test_linklocal_in6_addr}; + wil::network::socket_address test_v6_linklocal_addr_with_port{&Test_linklocal_in6_addr, TestPort}; - wil::networking::socket_address test_v4_any_addr{&Test_any_in_addr}; - wil::networking::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; + wil::network::socket_address test_v4_any_addr{&Test_any_in_addr}; + wil::network::socket_address test_v4_any_addr_with_port{&Test_any_in_addr, TestPort}; - wil::networking::socket_address test_v6_any_addr{&Test_any_in6_addr}; - wil::networking::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; + wil::network::socket_address test_v6_any_addr{&Test_any_in6_addr}; + wil::network::socket_address test_v6_any_addr_with_port{&Test_any_in6_addr, TestPort}; - wil::networking::socket_address test_v4_loopback_addr{&Test_loopback_in_addr}; - wil::networking::socket_address test_v4_loopback_addr_with_port{&Test_loopback_in_addr, TestPort}; + wil::network::socket_address test_v4_loopback_addr{&Test_loopback_in_addr}; + wil::network::socket_address test_v4_loopback_addr_with_port{&Test_loopback_in_addr, TestPort}; - wil::networking::socket_address test_v6_loopback_addr{&Test_loopback_in6_addr}; - wil::networking::socket_address test_v6_loopback_addr_with_port{&Test_loopback_in6_addr, TestPort}; + wil::network::socket_address test_v6_loopback_addr{&Test_loopback_in6_addr}; + wil::network::socket_address test_v6_loopback_addr_with_port{&Test_loopback_in6_addr, TestPort}; // need WSAStartup called for some functions below - auto wsa_startup_tracking = wil::networking::WSAStartup_nothrow(); + auto wsa_startup_tracking = wil::network::WSAStartup_nothrow(); REQUIRE(static_cast(wsa_startup_tracking)); - using wil::networking::equals; + using wil::network::equals; SECTION("verify set_address_any") { - const auto VerifyV4AnyAddress = [&](const wil::networking::socket_address& v4_address, bool with_port) { - wil::networking::socket_address_wstring any_address_test_string{}; + const auto VerifyV4AnyAddress = [&](const wil::network::socket_address& v4_address, bool with_port) { + wil::network::socket_address_wstring any_address_test_string{}; REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(any_address_test_string))); REQUIRE(0 == memcmp(Test_any_in_addr_string, any_address_test_string, sizeof(Test_any_in_addr_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -852,7 +852,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } // also char* versions - wil::networking::socket_address_string any_address_test_char_string{}; + wil::network::socket_address_string any_address_test_char_string{}; REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(any_address_test_char_string))); REQUIRE(0 == memcmp(Test_any_in_addr_char_string, any_address_test_char_string, sizeof(Test_any_in_addr_char_string))); @@ -869,8 +869,8 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } }; - const auto VerifyV6AnyAddress = [&](const wil::networking::socket_address& v6_address, bool with_port) { - wil::networking::socket_address_wstring any_address_test_string{}; + const auto VerifyV6AnyAddress = [&](const wil::network::socket_address& v6_address, bool with_port) { + wil::network::socket_address_wstring any_address_test_string{}; REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(any_address_test_string))); REQUIRE(0 == memcmp(Test_any_in6_addr_string, any_address_test_string, sizeof(Test_any_in6_addr_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -896,7 +896,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } // also char* versions - wil::networking::socket_address_string any_address_test_char_string{}; + wil::network::socket_address_string any_address_test_char_string{}; REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(any_address_test_char_string))); REQUIRE(0 == memcmp(Test_any_in6_addr_char_string, any_address_test_char_string, sizeof(Test_any_in6_addr_char_string))); @@ -913,7 +913,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } }; - wil::networking::socket_address v4_address; + wil::network::socket_address v4_address; v4_address.set_address_any(AF_INET); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == 0); @@ -942,7 +942,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address == test_v6_any_addr_with_port); VerifyV6AnyAddress(v4_address, true); - wil::networking::socket_address v6_address; + wil::network::socket_address v6_address; v6_address.set_address_any(AF_INET6); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == 0); @@ -971,7 +971,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address == test_v4_any_addr_with_port); VerifyV4AnyAddress(v6_address, true); - wil::networking::socket_address defaulted_v4_address{AF_INET}; + wil::network::socket_address defaulted_v4_address{AF_INET}; defaulted_v4_address.set_address_any(); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == 0); @@ -990,7 +990,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(defaulted_v4_address == test_v4_any_addr_with_port); VerifyV4AnyAddress(defaulted_v4_address, true); - wil::networking::socket_address defaulted_v6_address{AF_INET6}; + wil::network::socket_address defaulted_v6_address{AF_INET6}; defaulted_v6_address.set_address_any(); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == 0); @@ -1012,8 +1012,8 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") SECTION("verify set_address_loopback") { - const auto VerifyV4LoopbackAddress = [&](const wil::networking::socket_address& v4_address, bool with_port) { - wil::networking::socket_address_wstring loopback_address_test_string{}; + const auto VerifyV4LoopbackAddress = [&](const wil::network::socket_address& v4_address, bool with_port) { + wil::network::socket_address_wstring loopback_address_test_string{}; REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(loopback_address_test_string))); REQUIRE(0 == memcmp(Test_loopback_in_addr_string, loopback_address_test_string, sizeof(Test_loopback_in_addr_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1039,8 +1039,8 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } }; - const auto VerifyV6LoopbackAddress = [&](const wil::networking::socket_address& v6_address, bool with_port) { - wil::networking::socket_address_wstring loopback_address_test_string{}; + const auto VerifyV6LoopbackAddress = [&](const wil::network::socket_address& v6_address, bool with_port) { + wil::network::socket_address_wstring loopback_address_test_string{}; REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(loopback_address_test_string))); REQUIRE(0 == memcmp(Test_loopback_in6_addr_string, loopback_address_test_string, sizeof(Test_loopback_in6_addr_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1066,7 +1066,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") } }; - wil::networking::socket_address v4_address; + wil::network::socket_address v4_address; v4_address.set_address_loopback(AF_INET); REQUIRE(v4_address.family() == AF_INET); REQUIRE(v4_address.port() == 0); @@ -1095,7 +1095,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address == test_v6_loopback_addr_with_port); VerifyV6LoopbackAddress(v4_address, true); - wil::networking::socket_address v6_address; + wil::network::socket_address v6_address; v6_address.set_address_loopback(AF_INET6); REQUIRE(v6_address.family() == AF_INET6); REQUIRE(v6_address.port() == 0); @@ -1124,7 +1124,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address == test_v4_loopback_addr_with_port); VerifyV4LoopbackAddress(v6_address, true); - wil::networking::socket_address defaulted_v4_address{AF_INET}; + wil::network::socket_address defaulted_v4_address{AF_INET}; defaulted_v4_address.set_address_loopback(); REQUIRE(defaulted_v4_address.family() == AF_INET); REQUIRE(defaulted_v4_address.port() == 0); @@ -1143,7 +1143,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(defaulted_v4_address == test_v4_loopback_addr_with_port); VerifyV4LoopbackAddress(defaulted_v4_address, true); - wil::networking::socket_address defaulted_v6_address{AF_INET6}; + wil::network::socket_address defaulted_v6_address{AF_INET6}; defaulted_v6_address.set_address_loopback(); REQUIRE(defaulted_v6_address.family() == AF_INET6); REQUIRE(defaulted_v6_address.port() == 0); @@ -1165,7 +1165,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") SECTION("verify v4 set_sockaddr_nothrow") { - wil::networking::socket_address v4_address; + wil::network::socket_address v4_address; v4_address.set_address_loopback(AF_INET); v4_address.set_port(TestPort); REQUIRE(v4_address.address_type() == NlatUnicast); @@ -1178,7 +1178,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address.address_type() == NlatUnicast); REQUIRE(!v4_address.is_address_loopback()); - wil::networking::socket_address_wstring v4_address_string{}; + wil::network::socket_address_wstring v4_address_string{}; REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_string))); REQUIRE(0 == memcmp(Test_in_addr_string, v4_address_string, sizeof(Test_in_addr_string))); @@ -1197,7 +1197,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address.address_type() == NlatUnicast); REQUIRE(!v4_address.is_address_loopback()); - wil::networking::socket_address_string v4_address_char_string{}; + wil::network::socket_address_string v4_address_char_string{}; REQUIRE(SUCCEEDED(v4_address.write_address_nothrow(v4_address_char_string))); REQUIRE(0 == memcmp(Test_in_addr_char_string, v4_address_char_string, sizeof(Test_in_addr_char_string))); @@ -1211,14 +1211,14 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(0 == memcmp(Test_in_addr_char_string2, v4_address_char_string, sizeof(Test_in_addr_char_string2))); // set_sockaddr via a SOCKET bound to an address - ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(test_socket.get() != INVALID_SOCKET); - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; test_address.set_address_loopback(AF_INET); test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1244,7 +1244,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") SECTION("verify v4 set_sockaddr throwing version") { #ifdef WIL_ENABLE_EXCEPTIONS - wil::networking::socket_address v4_address; + wil::network::socket_address v4_address; v4_address.set_address_loopback(AF_INET); v4_address.set_port(TestPort); REQUIRE(v4_address.address_type() == NlatUnicast); @@ -1289,14 +1289,14 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v4_address_string == Test_in_addr_string2); // set_sockaddr via a SOCKET bound to an address - ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(test_socket.get() != INVALID_SOCKET); - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; test_address.set_address_loopback(AF_INET); test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1322,7 +1322,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") SECTION("verify v6 set_sockaddr_nothrow") { - wil::networking::socket_address v6_address; + wil::network::socket_address v6_address; v6_address.set_address_loopback(AF_INET6); v6_address.set_port(TestPort); REQUIRE(v6_address.address_type() == NlatUnicast); @@ -1335,7 +1335,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address.address_type() == NlatUnicast); REQUIRE(!v6_address.is_address_loopback()); - wil::networking::socket_address_wstring v6_address_string{}; + wil::network::socket_address_wstring v6_address_string{}; REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_string))); REQUIRE(0 == memcmp(Test_in6_addr_string, v6_address_string, sizeof(Test_in6_addr_string))); @@ -1354,7 +1354,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address.address_type() == NlatUnicast); REQUIRE(!v6_address.is_address_loopback()); - wil::networking::socket_address_string v6_address_char_string{}; + wil::network::socket_address_string v6_address_char_string{}; REQUIRE(SUCCEEDED(v6_address.write_address_nothrow(v6_address_char_string))); REQUIRE(0 == memcmp(Test_in6_addr_char_string, v6_address_char_string, sizeof(Test_in6_addr_char_string))); @@ -1368,14 +1368,14 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(0 == memcmp(Test_in6_addr_char_string2, v6_address_char_string, sizeof(Test_in6_addr_char_string2))); // set_sockaddr via a SOCKET bound to an address - ::wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(test_socket.get() != INVALID_SOCKET); - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; test_address.set_address_loopback(AF_INET6); test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1401,7 +1401,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") SECTION("verify v6 set_sockaddr throwing version") { #ifdef WIL_ENABLE_EXCEPTIONS - wil::networking::socket_address v6_address; + wil::network::socket_address v6_address; v6_address.set_address_loopback(AF_INET6); v6_address.set_port(TestPort); REQUIRE(v6_address.address_type() == NlatUnicast); @@ -1446,14 +1446,14 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") REQUIRE(v6_address_string == Test_in6_addr_string2); // set_sockaddr via a SOCKET bound to an address - ::wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket test_socket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(test_socket.get() != INVALID_SOCKET); - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; test_address.set_address_loopback(AF_INET6); test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), ::wil::networking::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1484,7 +1484,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); - ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + wil::network::socket_address v4_addr{&v4_test_sockaddr}; REQUIRE(v4_addr.family() == AF_INET); REQUIRE(v4_addr.address_type() == NlatUnicast); REQUIRE(v4_addr.is_address_linklocal()); @@ -1507,7 +1507,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v6_test_sockaddr.sin6_scope_id = htonl(234567); memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); - ::wil::networking::socket_address v6_addr{&v6_test_sockaddr}; + wil::network::socket_address v6_addr{&v6_test_sockaddr}; REQUIRE(v6_addr.family() == AF_INET6); REQUIRE(v6_addr.address_type() == NlatUnicast); REQUIRE(v6_addr.is_address_linklocal()); @@ -1537,7 +1537,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order memcpy(&v4_test_sockaddr.sin_addr, &Test_linklocal_in_addr, sizeof(in_addr)); - ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + wil::network::socket_address v4_addr{&v4_test_sockaddr}; REQUIRE(v4_addr.family() == AF_INET); REQUIRE(v4_addr.address_type() == NlatUnicast); REQUIRE(v4_addr.is_address_linklocal()); @@ -1555,7 +1555,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v6_test_sockaddr.sin6_scope_id = htonl(234567); memcpy(&v6_test_sockaddr.sin6_addr, &Test_linklocal_in6_addr, sizeof(in6_addr)); - ::wil::networking::socket_address v6_addr{&v6_test_sockaddr}; + wil::network::socket_address v6_addr{&v6_test_sockaddr}; REQUIRE(v6_addr.family() == AF_INET6); REQUIRE(v6_addr.address_type() == NlatUnicast); REQUIRE(v6_addr.is_address_linklocal()); @@ -1568,7 +1568,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") #endif // swap v4 and v6 - wil::networking::swap(v4_addr, v6_addr); + wil::network::swap(v4_addr, v6_addr); // verify each has the others' properties REQUIRE(v6_addr.family() == AF_INET); @@ -1601,9 +1601,9 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") v4_test_sockaddr.sin_port = htons(TestPort); // the raw value is in network byte order memcpy(&v4_test_sockaddr.sin_addr, &Test_in_addr, sizeof(in_addr)); - ::wil::networking::socket_address v4_addr{&v4_test_sockaddr}; + wil::network::socket_address v4_addr{&v4_test_sockaddr}; - ::wil::networking::socket_address mapped_addr = ::wil::networking::map_dual_mode_4to6(v4_addr); + wil::network::socket_address mapped_addr = wil::network::map_dual_mode_4to6(v4_addr); REQUIRE(mapped_addr.family() == AF_INET6); REQUIRE(IN6_IS_ADDR_V4MAPPED(mapped_addr.in6_addr())); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1617,17 +1617,17 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") { InitTestAddresses(); - auto wsa_startup_tracking = wil::networking::WSAStartup_nothrow(); + auto wsa_startup_tracking = wil::network::WSAStartup_nothrow(); REQUIRE(static_cast(wsa_startup_tracking)); SECTION("verify set_sockaddr socket failure path") { // set_sockaddr via a SOCKET bound to an address - but this time do not call bind - ::wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket test_socket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(test_socket.get() != INVALID_SOCKET); - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; REQUIRE(FAILED(test_address.set_sockaddr_nothrow(test_socket.get()))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1651,7 +1651,7 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") SECTION("verify set_sockaddr_nothrow bad-address-string failure path") { - ::wil::networking::socket_address test_address; + wil::network::socket_address test_address; REQUIRE(FAILED(test_address.set_sockaddr_nothrow(L"abcdefg"))); REQUIRE(FAILED(test_address.set_sockaddr_nothrow("abcdefg"))); @@ -1690,12 +1690,39 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") #endif } + SECTION("verify write_address_nothrow AF_UNSPEC") + { + wil::network::socket_address af_unspec_address; + af_unspec_address.reset(AF_UNSPEC); + + wil::network::socket_address_string string_address; + REQUIRE(SUCCEEDED(af_unspec_address.write_address_nothrow(string_address))); + REQUIRE(L'\0' == wstring_address[0]); + REQUIRE(SUCCEEDED(af_unspec_address.write_complete_address_nothrow(string_address))); + REQUIRE(L'\0' == wstring_address[0]); + + wil::network::socket_address_wstring wstring_address; + REQUIRE(SUCCEEDED(af_unspec_address.write_address_nothrow(wstring_address))); + REQUIRE(L'\0' == wstring_address[0]); + REQUIRE(SUCCEEDED(af_unspec_address.write_complete_address_nothrow(wstring_address))); + REQUIRE(L'\0' == wstring_address[0]); + +#ifdef WIL_ENABLE_EXCEPTIONS + std::wstring test_string = af_unspec_address.write_address(); + REQUIRE(test_string.empty()); + + std::wstring test_string = af_unspec_address.write_complete_address(); + REQUIRE(test_string.empty()); +#endif + } + SECTION("verify write_address_nothrow failure path") { - ::wil::networking::socket_address test_address; - test_address.reset(AF_UNSPEC); + wil::network::socket_address test_address; + // set an unsupported family for verifying failure paths + test_address.reset(AF_APPLETALK); - ::wil::networking::socket_address_wstring wstring_address; + wil::network::socket_address_wstring wstring_address; REQUIRE(FAILED(test_address.write_address_nothrow(wstring_address))); REQUIRE(FAILED(test_address.write_complete_address_nothrow(wstring_address))); @@ -1737,7 +1764,7 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") REQUIRE(exception_thrown); #endif - ::wil::networking::socket_address_string string_address; + wil::network::socket_address_string string_address; REQUIRE(FAILED(test_address.write_address_nothrow(string_address))); REQUIRE(FAILED(test_address.write_complete_address_nothrow(string_address))); } @@ -1745,13 +1772,13 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") SECTION("verify write_address_nothrow maximum string size") { { - ::wil::networking::socket_address test_mapped_address; + wil::network::socket_address test_mapped_address; REQUIRE(SUCCEEDED(test_mapped_address.set_sockaddr_nothrow(L"0000:0000:0000:0000:0000:ffff:255.255.255.255"))); test_mapped_address.set_port(std::numeric_limits::max()); test_mapped_address.set_scope_id(std::numeric_limits::max()); test_mapped_address.set_flow_info(std::numeric_limits::max()); - ::wil::networking::socket_address_wstring test_mapped_address_string; + wil::network::socket_address_wstring test_mapped_address_string; REQUIRE(SUCCEEDED(test_mapped_address.write_address_nothrow(test_mapped_address_string))); REQUIRE(SUCCEEDED(test_mapped_address.write_complete_address_nothrow(test_mapped_address_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1762,13 +1789,13 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") } { - ::wil::networking::socket_address test_max_v6_address; + wil::network::socket_address test_max_v6_address; REQUIRE(SUCCEEDED(test_max_v6_address.set_sockaddr_nothrow(L"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); test_max_v6_address.set_port(std::numeric_limits::max()); test_max_v6_address.set_scope_id(std::numeric_limits::max()); test_max_v6_address.set_flow_info(std::numeric_limits::max()); - ::wil::networking::socket_address_wstring test_max_v6_address_string; + wil::network::socket_address_wstring test_max_v6_address_string; REQUIRE(SUCCEEDED(test_max_v6_address.write_address_nothrow(test_max_v6_address_string))); REQUIRE(SUCCEEDED(test_max_v6_address.write_complete_address_nothrow(test_max_v6_address_string))); #ifdef WIL_ENABLE_EXCEPTIONS @@ -1782,12 +1809,12 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") { - using wil::networking::equals; + using wil::network::equals; SECTION("verify winsock_extension_function_table") { // verify the first 3 function pointers are calling through correctly to confirm the function table is correct - ::wil::networking::winsock_extension_function_table test_table = ::wil::networking::winsock_extension_function_table::load(); + wil::network::winsock_extension_function_table test_table = wil::network::winsock_extension_function_table::load(); REQUIRE(static_cast(test_table)); REQUIRE(test_table.f.AcceptEx); REQUIRE(test_table.f.ConnectEx); @@ -1799,14 +1826,14 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(test_table.f.WSASendMsg); // create a listening socket and post an AcceptEx on it - ::wil::unique_socket listeningSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket listeningSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(listeningSocket.get() != INVALID_SOCKET); - ::wil::networking::socket_address listenAddress{AF_INET6}; + wil::network::socket_address listenAddress{AF_INET6}; listenAddress.set_address_loopback(); listenAddress.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(listeningSocket.get(), listenAddress.sockaddr(), ::wil::networking::socket_address::length); + auto bind_error = ::bind(listeningSocket.get(), listenAddress.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1824,13 +1851,13 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(listen_error == 0); // the buffer to supply to AcceptEx to capture the address information - static constexpr size_t singleAddressOutputBufferSize = ::wil::networking::socket_address::length + 16; + static constexpr size_t singleAddressOutputBufferSize = wil::network::socket_address::length + 16; char acceptex_output_buffer[singleAddressOutputBufferSize * 2]{}; - ::wil::unique_socket acceptSocket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket acceptSocket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(acceptSocket.get() != INVALID_SOCKET); DWORD acceptex_bytes_received{}; - ::wil::unique_event_nothrow acceptex_overlapped_event{}; + wil::unique_event_nothrow acceptex_overlapped_event{}; REQUIRE(SUCCEEDED(acceptex_overlapped_event.create())); REQUIRE(acceptex_overlapped_event.get() != nullptr); OVERLAPPED acceptex_overlapped{}; @@ -1855,7 +1882,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(gle == ERROR_IO_PENDING); // ensure that if this test function fails (returns) before AcceptEx completes asynchronously // that we wait for this async (overlapped) call to complete - const auto ensure_acceptex_overlapped_completes = ::wil::scope_exit([&] { + const auto ensure_acceptex_overlapped_completes = wil::scope_exit([&] { // close the sockets to cancel any pended IO acceptSocket.reset(); listeningSocket.reset(); @@ -1864,21 +1891,21 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") }); // now create a socket to connect to it - ::wil::unique_event_nothrow connectex_overlapped_event{}; + wil::unique_event_nothrow connectex_overlapped_event{}; REQUIRE(SUCCEEDED(connectex_overlapped_event.create())); REQUIRE(connectex_overlapped_event.get() != nullptr); OVERLAPPED connectex_overlapped{}; connectex_overlapped.hEvent = connectex_overlapped_event.get(); - ::wil::unique_socket connectingSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; + wil::unique_socket connectingSocket{::socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(connectingSocket.get() != INVALID_SOCKET); // ConnectEx requires a bound socket - ::wil::networking::socket_address connecting_from_address{AF_INET6}; + wil::network::socket_address connecting_from_address{AF_INET6}; connecting_from_address.set_address_loopback(); connecting_from_address.set_port(0); // just an ephemeral port, ConnectEx will find a port gle = 0; - bind_error = ::bind(connectingSocket.get(), connecting_from_address.sockaddr(), ::wil::networking::socket_address::length); + bind_error = ::bind(connectingSocket.get(), connecting_from_address.sockaddr(), wil::network::socket_address::length); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1888,7 +1915,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") gle = 0; auto connectex_return = test_table.f.ConnectEx( - connectingSocket.get(), listenAddress.sockaddr(), ::wil::networking::socket_address::length, nullptr, 0, nullptr, &connectex_overlapped); + connectingSocket.get(), listenAddress.sockaddr(), wil::network::socket_address::length, nullptr, 0, nullptr, &connectex_overlapped); if (!connectex_return) { gle = ::WSAGetLastError(); @@ -1898,7 +1925,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(gle == ERROR_IO_PENDING); // ensure that if this test function fails (returns) before ConnectEx completes asynchronously // that we wait for this async (overlapped) call to complete - const auto ensure_connectex_overlapped_completes = ::wil::scope_exit([&] { + const auto ensure_connectex_overlapped_completes = wil::scope_exit([&] { // close the socket to cancel any pended IO connectingSocket.reset(); // now wait for our async call @@ -1937,7 +1964,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(acceptex_overlapped_result == TRUE); // issue a DisconnectEx from the client - ::wil::unique_event_nothrow disconnectex_overlapped_event{}; + wil::unique_event_nothrow disconnectex_overlapped_event{}; REQUIRE(SUCCEEDED(disconnectex_overlapped_event.create())); REQUIRE(disconnectex_overlapped_event.get() != nullptr); OVERLAPPED disconnectex_overlapped{}; @@ -1974,7 +2001,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") SECTION("verify rio_extension_function_table") { // verify 2 function pointers are calling through correctly to confirm the function table is correct - ::wil::networking::rio_extension_function_table test_table = ::wil::networking::rio_extension_function_table::load(); + wil::network::rio_extension_function_table test_table = wil::network::rio_extension_function_table::load(); REQUIRE(static_cast(test_table)); REQUIRE(test_table.f.cbSize > 0); REQUIRE(test_table.f.RIOReceive); @@ -1991,7 +2018,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(test_table.f.RIOResizeCompletionQueue); REQUIRE(test_table.f.RIOResizeRequestQueue); - ::wil::unique_event_nothrow rio_completion_notification_event{}; + wil::unique_event_nothrow rio_completion_notification_event{}; REQUIRE(SUCCEEDED(rio_completion_notification_event.create())); REQUIRE(rio_completion_notification_event.get() != nullptr); @@ -2017,15 +2044,15 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { - using wil::networking::addr_info; - using wil::networking::equals; + using wil::network::addr_info; + using wil::network::equals; - const auto cleanup = wil::networking::WSAStartup_nothrow(); + const auto cleanup = wil::network::WSAStartup_nothrow(); InitTestAddresses(); SECTION("verify resolve_local_addresses") { - addr_info test_addr = wil::networking::resolve_local_addresses_nothrow(); + addr_info test_addr = wil::network::resolve_local_addresses_nothrow(); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); @@ -2035,14 +2062,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE((family == AF_INET || family == AF_INET6)); REQUIRE(!address.is_address_loopback()); - wil::networking::socket_address_wstring address_string; + wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); // wprintf(L"... resolve_local_addresses_nothrow : %ws\n", address_string); } #ifdef WIL_ENABLE_EXCEPTIONS { - test_addr = wil::networking::resolve_local_addresses(); + test_addr = wil::network::resolve_local_addresses(); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); @@ -2061,7 +2088,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_localhost_addresses") { - addr_info test_addr = wil::networking::resolve_localhost_addresses_nothrow(); + addr_info test_addr = wil::network::resolve_localhost_addresses_nothrow(); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); // verify operator-> @@ -2087,14 +2114,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE(false); } - wil::networking::socket_address_wstring address_string; + wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); // wprintf(L"... resolve_localhost_addresses_nothrow : %ws\n", address_string); } #ifdef WIL_ENABLE_EXCEPTIONS { - test_addr = wil::networking::resolve_localhost_addresses(); + test_addr = wil::network::resolve_localhost_addresses(); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); // verify operator-> @@ -2129,7 +2156,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_name") { - addr_info test_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + addr_info test_addr = wil::network::resolve_name_nothrow(L"..localmachine"); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); @@ -2139,14 +2166,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE((family == AF_INET || family == AF_INET6)); REQUIRE(!address.is_address_loopback()); - wil::networking::socket_address_wstring address_string; + wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); // wprintf(L"... resolve_name_nothrow(..localmachine) : %ws\n", address_string); } #ifdef WIL_ENABLE_EXCEPTIONS { - test_addr = wil::networking::resolve_name(L"..localmachine"); + test_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(test_addr.get_last_error() == 0); REQUIRE(test_addr.begin() != test_addr.end()); @@ -2165,7 +2192,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify const addr_info iterators") { - const addr_info test_addr = wil::networking::resolve_name_nothrow(L"localhost"); + const addr_info test_addr = wil::network::resolve_name_nothrow(L"localhost"); REQUIRE(test_addr.begin() != test_addr.end()); const addr_info::iterator test_iterator = test_addr.begin(); @@ -2177,7 +2204,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info iterator increment") { - const addr_info initial_addr = wil::networking::resolve_name_nothrow(L"localhost"); + const addr_info initial_addr = wil::network::resolve_name_nothrow(L"localhost"); REQUIRE(initial_addr.begin() != initial_addr.end()); auto total_count = 0; @@ -2186,7 +2213,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") ++total_count; } - addr_info test_addr = wil::networking::resolve_name_nothrow(L"localhost"); + addr_info test_addr = wil::network::resolve_name_nothrow(L"localhost"); REQUIRE(initial_addr.begin() != initial_addr.end()); addr_info::iterator test_iterator = test_addr.begin(); @@ -2196,7 +2223,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info iterator move behavior") { - addr_info moved_from_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + addr_info moved_from_addr = wil::network::resolve_name_nothrow(L"..localmachine"); REQUIRE(moved_from_addr.get_last_error() == 0); REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); @@ -2213,7 +2240,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE((family == AF_INET || family == AF_INET6)); REQUIRE(!address.is_address_loopback()); - wil::networking::socket_address_wstring address_string; + wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); // wprintf(L"... moved resolve_name_nothrow(..localmachine) : %ws\n", address_string); } @@ -2221,11 +2248,11 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info iterator move assignment behavior") { - addr_info moved_from_addr = wil::networking::resolve_name_nothrow(L"..localmachine"); + addr_info moved_from_addr = wil::network::resolve_name_nothrow(L"..localmachine"); REQUIRE(moved_from_addr.get_last_error() == 0); REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); - addr_info moved_to_addr{wil::networking::resolve_local_addresses_nothrow()}; + addr_info moved_to_addr{wil::network::resolve_local_addresses_nothrow()}; moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now @@ -2244,14 +2271,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE((family == AF_INET || family == AF_INET6)); REQUIRE(!address.is_address_loopback()); - wil::networking::socket_address_wstring address_string; + wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); } } SECTION("verify addr_info resolve_name failure") { - addr_info test_addr = wil::networking::resolve_name_nothrow(L"...xyz.xyz..."); + addr_info test_addr = wil::network::resolve_name_nothrow(L"...xyz.xyz..."); REQUIRE(test_addr.get_last_error() == WSAHOST_NOT_FOUND); REQUIRE(test_addr.begin() == test_addr.end()); @@ -2259,7 +2286,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") bool exception_thrown = false; try { - test_addr = wil::networking::resolve_name(L"...xyz.xyz..."); + test_addr = wil::network::resolve_name(L"...xyz.xyz..."); } catch (const wil::ResultException& e) { From ea0f1c33a6564dabecf812c7b5a17a627b3c9f7f Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 11 Jan 2025 18:00:11 -0800 Subject: [PATCH 13/22] Addressing feedback --- include/wil/network.h | 193 +++++++++++++++++++++----------------- tests/NetworkingTests.cpp | 7 +- 2 files changed, 113 insertions(+), 87 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 5d65a7f6..9fbd1c8f 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -396,11 +396,11 @@ namespace network // When using dual-mode sockets, one might need to connect to a target IPv4 address. // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address - inline ::wil::network::socket_address map_dual_mode_4to6(const ::wil::network::socket_address& inV4) WI_NOEXCEPT + inline wil::network::socket_address map_dual_mode_4to6(const wil::network::socket_address& inV4) WI_NOEXCEPT { constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; - ::wil::network::socket_address outV6{&v4MappedPrefix, inV4.port()}; + wil::network::socket_address outV6{&v4MappedPrefix, inV4.port()}; auto* const pIn6Addr{outV6.in6_addr()}; const auto* const pIn4Addr{inV4.in_addr()}; @@ -415,7 +415,7 @@ namespace network // // non-member swap // - inline void swap(::wil::network::socket_address& lhs, ::wil::network::socket_address& rhs) WI_NOEXCEPT + inline void swap(wil::network::socket_address& lhs, wil::network::socket_address& rhs) WI_NOEXCEPT { lhs.swap(rhs); } @@ -479,11 +479,11 @@ namespace network #if defined(_ITERATOR_) || defined(WIL_DOXYGEN) using iterator_category = ::std::forward_iterator_tag; #endif - using value_type = ::wil::network::socket_address; + using value_type = wil::network::socket_address; using difference_type = size_t; using distance_type = size_t; - using pointer = ::wil::network::socket_address*; - using reference = ::wil::network::socket_address&; + using pointer = wil::network::socket_address*; + using reference = wil::network::socket_address&; iterator(const ADDRINFOW* addr_info) WI_NOEXCEPT : m_addr_info(const_cast(addr_info)) @@ -501,22 +501,22 @@ namespace network iterator(iterator&&) WI_NOEXCEPT = default; iterator& operator=(iterator&&) WI_NOEXCEPT = default; - const ::wil::network::socket_address& operator*() const WI_NOEXCEPT + const wil::network::socket_address& operator*() const WI_NOEXCEPT { return m_socket_address; } - const ::wil::network::socket_address& operator*() WI_NOEXCEPT + const wil::network::socket_address& operator*() WI_NOEXCEPT { return m_socket_address; } - const ::wil::network::socket_address* operator->() const WI_NOEXCEPT + const wil::network::socket_address* operator->() const WI_NOEXCEPT { return &m_socket_address; } - const ::wil::network::socket_address* operator->() WI_NOEXCEPT + const wil::network::socket_address* operator->() WI_NOEXCEPT { return &m_socket_address; } @@ -567,7 +567,7 @@ namespace network private: // non-ownership of this pointer - the parent class must outlive the iterator ADDRINFOW* m_addr_info{nullptr}; - ::wil::network::socket_address m_socket_address{}; + wil::network::socket_address m_socket_address{}; }; iterator begin() const WI_NOEXCEPT @@ -654,45 +654,45 @@ namespace network // // socket_address definitions // - inline ::wil::network::socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT { reset(family); } template - ::wil::network::socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + wil::network::socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT { set_sockaddr(addr, inLength); } - inline ::wil::network::socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline ::wil::network::socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline ::wil::network::socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline ::wil::network::socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline ::wil::network::socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT { reset(AF_INET); set_address(addr); set_port(port); } - inline ::wil::network::socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline wil::network::socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT { reset(AF_INET6); set_address(addr); @@ -700,13 +700,13 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) - inline ::wil::network::socket_address::socket_address(PCWSTR addr, unsigned short port) + inline wil::network::socket_address::socket_address(PCWSTR addr, unsigned short port) { set_sockaddr(addr); set_port(port); } #endif - inline bool ::wil::network::socket_address::operator==(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool wil::network::socket_address::operator==(const wil::network::socket_address& rhs) const WI_NOEXCEPT { const auto& lhs{*this}; @@ -725,12 +725,12 @@ namespace network return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) == 0; } - inline bool ::wil::network::socket_address::operator!=(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool wil::network::socket_address::operator!=(const wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this == rhs); } - inline bool ::wil::network::socket_address::operator<(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool wil::network::socket_address::operator<(const wil::network::socket_address& rhs) const WI_NOEXCEPT { const auto& lhs{*this}; @@ -809,12 +809,12 @@ namespace network } } - inline bool ::wil::network::socket_address::operator>(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool wil::network::socket_address::operator>(const wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this < rhs); } - inline void ::wil::network::socket_address::swap(socket_address& addr) WI_NOEXCEPT + inline void wil::network::socket_address::swap(socket_address& addr) WI_NOEXCEPT { SOCKADDR_INET tempAddr{}; ::memcpy_s(&tempAddr, sizeof(tempAddr), &addr.m_sockaddr, sizeof(addr.m_sockaddr)); @@ -822,59 +822,60 @@ namespace network ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), &tempAddr, sizeof(tempAddr)); } - inline void ::wil::network::socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void wil::network::socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT { +#ifndef WI_NETWORK_TEST WI_ASSERT(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); - - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); +#endif + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); m_sockaddr.si_family = family; } template - void ::wil::network::socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + void wil::network::socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT { - WI_ASSERT(static_cast(inLength) <= ::wil::network::socket_address::length); + WI_ASSERT(static_cast(inLength) <= wil::network::socket_address::length); - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); if (addr) { ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, inLength); } } - inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); ::memcpy_s(&m_sockaddr.Ipv4, sizeof(m_sockaddr.Ipv4), addr, sizeof(*addr)); } - inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); ::memcpy_s(&m_sockaddr.Ipv6, sizeof(m_sockaddr.Ipv6), addr, sizeof(*addr)); } - inline void ::wil::network::socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, sizeof(*addr)); } - inline void ::wil::network::socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { FAIL_FAST_IF_MSG( - addr->lpSockaddr && addr->iSockaddrLength > ::wil::network::socket_address::length, + addr->lpSockaddr && addr->iSockaddrLength > wil::network::socket_address::length, "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", addr->iSockaddrLength); - ::memset(&m_sockaddr, 0, ::wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, wil::network::socket_address::length); if (addr->lpSockaddr) { ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr->lpSockaddr, addr->iSockaddrLength); } } - inline bool ::wil::network::socket_address::is_address_linklocal() const WI_NOEXCEPT + inline bool wil::network::socket_address::is_address_linklocal() const WI_NOEXCEPT { switch (family()) { @@ -893,7 +894,7 @@ namespace network } } - inline bool ::wil::network::socket_address::is_address_loopback() const WI_NOEXCEPT + inline bool wil::network::socket_address::is_address_loopback() const WI_NOEXCEPT { switch (family()) { @@ -931,7 +932,7 @@ namespace network } } - inline void ::wil::network::socket_address::set_port(USHORT port) WI_NOEXCEPT + inline void wil::network::socket_address::set_port(USHORT port) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET || family() == AF_INET6); @@ -942,7 +943,7 @@ namespace network m_sockaddr.Ipv4.sin_port = ::htons(port); } - inline void ::wil::network::socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT + inline void wil::network::socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -952,7 +953,7 @@ namespace network } } - inline void ::wil::network::socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT + inline void wil::network::socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -962,12 +963,12 @@ namespace network } } - inline void ::wil::network::socket_address::set_address_any() WI_NOEXCEPT + inline void wil::network::socket_address::set_address_any() WI_NOEXCEPT { set_address_any(family()); } - inline void ::wil::network::socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void wil::network::socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT { WI_ASSERT(family == AF_INET || family == AF_INET6); @@ -979,12 +980,12 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void ::wil::network::socket_address::set_address_loopback() WI_NOEXCEPT + inline void wil::network::socket_address::set_address_loopback() WI_NOEXCEPT { set_address_loopback(family()); } - inline void ::wil::network::socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void wil::network::socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT { // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); @@ -1005,7 +1006,7 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void ::wil::network::socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); @@ -1015,7 +1016,7 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void ::wil::network::socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT + inline void wil::network::socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -1026,27 +1027,27 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) - inline void ::wil::network::socket_address::set_sockaddr(SOCKET s) + inline void wil::network::socket_address::set_sockaddr(SOCKET s) { THROW_IF_FAILED(set_sockaddr_nothrow(s)); } - inline void ::wil::network::socket_address::set_sockaddr(PCWSTR address) + inline void wil::network::socket_address::set_sockaddr(PCWSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } - inline void ::wil::network::socket_address::set_sockaddr(PCSTR address) + inline void wil::network::socket_address::set_sockaddr(PCSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } #endif - inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT + inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT { reset(AF_UNSPEC); - auto nameLength{::wil::network::socket_address::length}; + auto nameLength{wil::network::socket_address::length}; auto error{::getsockname(s, sockaddr(), &nameLength)}; if (error != 0) { @@ -1056,7 +1057,7 @@ namespace network return S_OK; } - inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT + inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT { PCWSTR terminator_unused; @@ -1079,7 +1080,7 @@ namespace network return E_INVALIDARG; } - inline HRESULT ::wil::network::socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT + inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT { PCSTR terminator_unused; @@ -1103,19 +1104,26 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline ::std::wstring ::wil::network::socket_address::write_address() const + inline ::std::wstring wil::network::socket_address::write_address() const { - ::wil::network::socket_address_wstring returnString{}; + wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT ::wil::network::socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT wil::network::socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); + // socket_address will return an empty string for AF_UNSPEC (the default family type when constructed) + // as to not fail if just default constructed + if (family() == AF_UNSPEC) + { + return S_OK; + } + const void* const pAddr{family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; @@ -1129,10 +1137,17 @@ namespace network return S_OK; } - inline HRESULT ::wil::network::socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT wil::network::socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); + // socket_address will return an empty string for AF_UNSPEC (the default family type when constructed) + // as to not fail if just default constructed + if (family() == AF_UNSPEC) + { + return S_OK; + } + const void* const pAddr{family() == AF_INET ? static_cast(&m_sockaddr.Ipv4.sin_addr) : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; @@ -1147,30 +1162,32 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline ::std::wstring ::wil::network::socket_address::write_complete_address() const + inline ::std::wstring wil::network::socket_address::write_complete_address() const { - ::wil::network::socket_address_wstring returnString{}; + wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_complete_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT ::wil::network::socket_address::write_complete_address_nothrow( + inline HRESULT wil::network::socket_address::write_complete_address_nothrow( socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); + // socket_address will return an empty string for AF_UNSPEC (the default family type when constructed) // as to not fail if just default constructed if (family() == AF_UNSPEC) { return S_OK; } + // addressLength == # of chars, not bytes DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringW( const_cast(sockaddr()), - ::wil::network::socket_address::length, + wil::network::socket_address::length, nullptr, address, &addressLength) != 0) @@ -1182,13 +1199,21 @@ namespace network // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API #if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) - inline HRESULT ::wil::network::socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT wil::network::socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); + + // socket_address will return an empty string for AF_UNSPEC (the default family type when constructed) + // as to not fail if just default constructed + if (family() == AF_UNSPEC) + { + return S_OK; + } + DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringA( const_cast(sockaddr()), - ::wil::network::socket_address::length, + wil::network::socket_address::length, nullptr, address, &addressLength) != 0) @@ -1199,12 +1224,12 @@ namespace network } #endif - inline ADDRESS_FAMILY::wil::network::socket_address::family() const WI_NOEXCEPT + inline ADDRESS_FAMILY wil::network::socket_address::family() const WI_NOEXCEPT { return m_sockaddr.si_family; } - inline USHORT::wil::network::socket_address::port() const WI_NOEXCEPT + inline USHORT wil::network::socket_address::port() const WI_NOEXCEPT { switch (family()) { @@ -1220,7 +1245,7 @@ namespace network } } - inline ULONG::wil::network::socket_address::flow_info() const WI_NOEXCEPT + inline ULONG wil::network::socket_address::flow_info() const WI_NOEXCEPT { switch (family()) { @@ -1236,7 +1261,7 @@ namespace network } } - inline ULONG::wil::network::socket_address::scope_id() const WI_NOEXCEPT + inline ULONG wil::network::socket_address::scope_id() const WI_NOEXCEPT { switch (family()) { @@ -1252,69 +1277,69 @@ namespace network } } - inline SOCKADDR* ::wil::network::socket_address::sockaddr() WI_NOEXCEPT + inline SOCKADDR* wil::network::socket_address::sockaddr() WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline SOCKADDR_IN* ::wil::network::socket_address::sockaddr_in() WI_NOEXCEPT + inline SOCKADDR_IN* wil::network::socket_address::sockaddr_in() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline SOCKADDR_IN6* ::wil::network::socket_address::sockaddr_in6() WI_NOEXCEPT + inline SOCKADDR_IN6* wil::network::socket_address::sockaddr_in6() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline SOCKADDR_INET* ::wil::network::socket_address::sockaddr_inet() WI_NOEXCEPT + inline SOCKADDR_INET* wil::network::socket_address::sockaddr_inet() WI_NOEXCEPT { return &m_sockaddr; } - inline IN_ADDR* ::wil::network::socket_address::in_addr() WI_NOEXCEPT + inline IN_ADDR* wil::network::socket_address::in_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline IN6_ADDR* ::wil::network::socket_address::in6_addr() WI_NOEXCEPT + inline IN6_ADDR* wil::network::socket_address::in6_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } - inline const SOCKADDR* ::wil::network::socket_address::sockaddr() const WI_NOEXCEPT + inline const SOCKADDR* wil::network::socket_address::sockaddr() const WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline const SOCKADDR_IN* ::wil::network::socket_address::sockaddr_in() const WI_NOEXCEPT + inline const SOCKADDR_IN* wil::network::socket_address::sockaddr_in() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline const SOCKADDR_IN6* ::wil::network::socket_address::sockaddr_in6() const WI_NOEXCEPT + inline const SOCKADDR_IN6* wil::network::socket_address::sockaddr_in6() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline const SOCKADDR_INET* ::wil::network::socket_address::sockaddr_inet() const WI_NOEXCEPT + inline const SOCKADDR_INET* wil::network::socket_address::sockaddr_inet() const WI_NOEXCEPT { return &m_sockaddr; } - inline const IN_ADDR* ::wil::network::socket_address::in_addr() const WI_NOEXCEPT + inline const IN_ADDR* wil::network::socket_address::in_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline const IN6_ADDR* ::wil::network::socket_address::in6_addr() const WI_NOEXCEPT + inline const IN6_ADDR* wil::network::socket_address::in6_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index e1a84b19..d44badef 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -8,6 +8,7 @@ // set this to give access to all functions #define _WINSOCK_DEPRECATED_NO_WARNINGS +#define WI_NETWORK_TEST #include #include "common.h" @@ -1697,9 +1698,9 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") wil::network::socket_address_string string_address; REQUIRE(SUCCEEDED(af_unspec_address.write_address_nothrow(string_address))); - REQUIRE(L'\0' == wstring_address[0]); + REQUIRE('\0' == string_address[0]); REQUIRE(SUCCEEDED(af_unspec_address.write_complete_address_nothrow(string_address))); - REQUIRE(L'\0' == wstring_address[0]); + REQUIRE('\0' == string_address[0]); wil::network::socket_address_wstring wstring_address; REQUIRE(SUCCEEDED(af_unspec_address.write_address_nothrow(wstring_address))); @@ -1711,7 +1712,7 @@ TEST_CASE("NetworkingTests::Verifying_failure_paths", "[networking]") std::wstring test_string = af_unspec_address.write_address(); REQUIRE(test_string.empty()); - std::wstring test_string = af_unspec_address.write_complete_address(); + test_string = af_unspec_address.write_complete_address(); REQUIRE(test_string.empty()); #endif } From 6602bfa8e71fc969378ded5e2dd8d0e72ea9dc79 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 11 Jan 2025 23:02:57 -0800 Subject: [PATCH 14/22] Missed a variable init. --- include/wil/network.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 9fbd1c8f..2154f3b1 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -1439,7 +1439,7 @@ namespace network constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; constexpr DWORD bytes{sizeof(void*)}; - DWORD unused_bytes; + DWORD unused_bytes{}; if (::WSAIoctl( localSocket.get(), controlCode, @@ -1489,7 +1489,7 @@ namespace network ::memset(&table.f, 0, bytes); table.f.cbSize = bytes; - DWORD unused_bytes; + DWORD unused_bytes{}; if (::WSAIoctl( localSocket.get(), controlCode, From 15ecc5d46d47fc08daf50dfe2fc68bdec06a6622 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 11 Jan 2025 23:22:18 -0800 Subject: [PATCH 15/22] Simplifying the function to load the extension function pointers. --- include/wil/network.h | 129 ++++++++++-------------------------------- 1 file changed, 31 insertions(+), 98 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 2154f3b1..08a6647d 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -1149,8 +1149,8 @@ namespace network } const void* const pAddr{family() == AF_INET - ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; // the last param to InetNtopA is # of characters, not bytes const auto* error_value{::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN)}; @@ -1345,12 +1345,9 @@ namespace network return &m_sockaddr.Ipv6.sin6_addr; } - // - // definitions for winsock_extension_function_table - // inline winsock_extension_function_table winsock_extension_function_table::load() WI_NOEXCEPT { - winsock_extension_function_table table{}; + winsock_extension_function_table table; // if WSAStartup failed, immediately exit if (!table.wsa_reference_count) { @@ -1358,106 +1355,42 @@ namespace network } // we need a temporary socket for the IOCTL to load the functions - const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; if (INVALID_SOCKET == localSocket.get()) { return table; } - // walk WINSOCK_EXTENSION_FUNCTION_TABLE to load each function pointer - constexpr auto function_table_length{sizeof(table.f) / sizeof(void*)}; - static_assert(function_table_length == 8); - - bool successfully_loaded{true}; - for (auto fnLoop = 0ul; successfully_loaded && fnLoop < function_table_length; ++fnLoop) - { - void* functionPtr{}; - GUID extensionGuid{}; - - switch (fnLoop) - { - case 0: - { - constexpr GUID acceptex_guid = WSAID_ACCEPTEX; - extensionGuid = acceptex_guid; - functionPtr = &table.f.AcceptEx; - break; - } - case 1: - { - constexpr GUID connectex_guid = WSAID_CONNECTEX; - extensionGuid = connectex_guid; - functionPtr = &table.f.ConnectEx; - break; - } - case 2: - { - constexpr GUID disconnectex_guid = WSAID_DISCONNECTEX; - extensionGuid = disconnectex_guid; - functionPtr = &table.f.DisconnectEx; - break; - } - case 3: - { - constexpr GUID getacceptexsockaddrs_guid = WSAID_GETACCEPTEXSOCKADDRS; - extensionGuid = getacceptexsockaddrs_guid; - functionPtr = &table.f.GetAcceptExSockaddrs; - break; - } - case 4: - { - constexpr GUID transmitfile_guid = WSAID_TRANSMITFILE; - extensionGuid = transmitfile_guid; - functionPtr = &table.f.TransmitFile; - break; - } - case 5: - { - constexpr GUID transmitpackets_guid = WSAID_TRANSMITPACKETS; - extensionGuid = transmitpackets_guid; - functionPtr = &table.f.TransmitPackets; - break; - } - case 6: - { - constexpr GUID wsarecvmsg_guid = WSAID_WSARECVMSG; - extensionGuid = wsarecvmsg_guid; - functionPtr = &table.f.WSARecvMsg; - break; - } - case 7: - { - constexpr GUID wsasendmsg_guid = WSAID_WSASENDMSG; - extensionGuid = wsasendmsg_guid; - functionPtr = &table.f.WSASendMsg; - break; - } - - default: - FAIL_FAST(); - } - + const auto load_function_pointer = [](SOCKET localSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(void*)}; + constexpr DWORD bytes{sizeof(functionPtr)}; DWORD unused_bytes{}; - if (::WSAIoctl( - localSocket.get(), - controlCode, - &extensionGuid, - DWORD{sizeof(extensionGuid)}, - functionPtr, - bytes, - &unused_bytes, - nullptr, - nullptr) != 0) - { - LOG_IF_WIN32_ERROR(::WSAGetLastError()); + const auto error{::WSAIoctl( + localSocket, + controlCode, + &extensionGuid, + DWORD{sizeof(extensionGuid)}, + functionPtr, + bytes, + &unused_bytes, + nullptr, + nullptr)}; + return error == 0 ? S_OK : HRESULT_FROM_WIN32(::WSAGetLastError()); + }; - // if any failed to be found, something is very broken - // all should load, or all should fail - ::memset(&table.f, 0, sizeof(table.f)); - successfully_loaded = false; - } + // Load the functions into the table + if (FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_ACCEPTEX, &table.f.AcceptEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_CONNECTEX, &table.f.ConnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_DISCONNECTEX, &table.f.DisconnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_GETACCEPTEXSOCKADDRS, &table.f.GetAcceptExSockaddrs)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITFILE, &table.f.TransmitFile)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITPACKETS, &table.f.TransmitPackets)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSARECVMSG, &table.f.WSARecvMsg)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSASENDMSG, &table.f.WSASendMsg))) + { + // if any failed to be found, something is very broken + // all should load, or all should fail + ::memset(&table.f, 0, sizeof(table.f)); } return table; From ca07b3f06002e7adc7f1ff302fc88fa04afb1c41 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Wed, 15 Jan 2025 23:51:16 -0800 Subject: [PATCH 16/22] Simplified the addr_info RAII + iterator object -- now also supports all 4 addrinfo* structures. Need to add more tests for the other addrinfo types. --- include/wil/network.h | 117 ++++++++++++++++++------------------- tests/NetworkingTests.cpp | 118 ++++++++------------------------------ 2 files changed, 82 insertions(+), 153 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 08a6647d..1d37157d 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -423,38 +423,27 @@ namespace network //! class addr_info encapsulates the ADDRINFO structure //! this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo //! iterator semantics are supported to safely access these addresses - class addr_info + //! supports all addrinfo types: ADDRINFOA, ADDRINFOW, ADDRINFOEXA, ADDRINFOEXW +template + class addr_info_t { public: - addr_info(_In_ ADDRINFOW* addrResult, int error) WI_NOEXCEPT : - m_addrResult{addrResult}, - m_lastError{error} + addr_info_t(_In_ T* addrResult) WI_NOEXCEPT : m_addrResult{addrResult} { } - [[nodiscard]] int get_last_error() const WI_NOEXCEPT - { - return m_lastError; - } + // the d'tor calls the free function matching the addrinfo* type T + ~addr_info_t() WI_NOEXCEPT; + addr_info_t(const addr_info_t&) = delete; + addr_info_t& operator=(const addr_info_t&) = delete; - ~addr_info() WI_NOEXCEPT - { - if (m_addrResult) - { - ::FreeAddrInfoW(m_addrResult); - } - } - - addr_info(const addr_info&) = delete; - addr_info& operator=(const addr_info&) = delete; - - addr_info(addr_info&& rhs) WI_NOEXCEPT + addr_info_t(addr_info_t&& rhs) WI_NOEXCEPT { m_addrResult = rhs.m_addrResult; rhs.m_addrResult = nullptr; } - addr_info& operator=(addr_info&& rhs) WI_NOEXCEPT + addr_info_t& operator=(addr_info_t&& rhs) WI_NOEXCEPT { if (this != &rhs) { @@ -485,8 +474,8 @@ namespace network using pointer = wil::network::socket_address*; using reference = wil::network::socket_address&; - iterator(const ADDRINFOW* addr_info) WI_NOEXCEPT : - m_addr_info(const_cast(addr_info)) + iterator(const ADDRINFOW* addrinfo) WI_NOEXCEPT : + m_addr_info(const_cast(addrinfo)) { // must const cast so we can re-use this pointer as we walk the list if (m_addr_info) @@ -531,15 +520,20 @@ namespace network return !(*this == rhs); } - // pre-increment iterator& operator++() WI_NOEXCEPT { this->operator+=(1); return *this; } - // increment by integer - iterator& operator+=(size_t offset) + iterator operator++(int) WI_NOEXCEPT + { + auto return_value = *this; + this->operator+=(1); + return return_value; + } + + iterator& operator+=(size_t offset) WI_NOEXCEPT { for (size_t count = 0; count < offset; ++count) { @@ -561,12 +555,9 @@ namespace network return *this; } - // not supporting post-increment - which would require copy-construction - iterator operator++(int) = delete; - private: // non-ownership of this pointer - the parent class must outlive the iterator - ADDRINFOW* m_addr_info{nullptr}; + T* m_addr_info{nullptr}; wil::network::socket_address m_socket_address{}; }; @@ -581,43 +572,49 @@ namespace network } private: - ADDRINFOW* m_addrResult{}; - int m_lastError{}; + T* m_addrResult{}; }; - //! wil function to capture resolving a name to a set of IP addresses - //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::network::addr_info resolve_name_nothrow(_In_ PCWSTR name) WI_NOEXCEPT + typedef network::addr_info_t addr_info_a; + template<> + inline addr_info_t::~addr_info_t() WI_NOEXCEPT { - int lastError{0}; - ADDRINFOW* addrResult{}; - if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) + if (m_addrResult) { - lastError = ::WSAGetLastError(); + ::freeaddrinfo(m_addrResult); } - - return {addrResult, lastError}; } - - //! wil function to capture resolving the local machine to its set of IP addresses - //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::network::addr_info resolve_local_addresses_nothrow() WI_NOEXCEPT + typedef network::addr_info_t addr_info; + template<> + inline addr_info_t::~addr_info_t() WI_NOEXCEPT { - return ::wil::network::resolve_name_nothrow(L""); + if (m_addrResult) + { + ::FreeAddrInfoW(m_addrResult); + } } - - //! wil function to capture resolving the local-host addresses - //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - //! the returned RAII object exposes get_last_error() to check for errors - inline ::wil::network::addr_info resolve_localhost_addresses_nothrow() WI_NOEXCEPT + // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API +#if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) + typedef network::addr_info_t addr_info_exa; + template<> + inline addr_info_t::~addr_info_t() WI_NOEXCEPT { - return ::wil::network::resolve_name_nothrow(L"localhost"); + if (m_addrResult) + { + ::FreeAddrInfoEx(m_addrResult); + } } +#endif + typedef network::addr_info_t addr_infoex; + template<> + inline addr_info_t::~addr_info_t() WI_NOEXCEPT + { + if (m_addrResult) + { + ::FreeAddrInfoExW(m_addrResult); + } + } + #if defined(WIL_ENABLE_EXCEPTIONS) //! wil function to capture resolving a name to a set of IP addresses, throwing on error @@ -631,7 +628,7 @@ namespace network THROW_WIN32(::WSAGetLastError()); } - return {addrResult, NO_ERROR}; + return {addrResult}; } //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error @@ -1361,12 +1358,12 @@ namespace network return table; } - const auto load_function_pointer = [](SOCKET localSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { + const auto load_function_pointer = [](SOCKET lambdaSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; constexpr DWORD bytes{sizeof(functionPtr)}; DWORD unused_bytes{}; const auto error{::WSAIoctl( - localSocket, + lambdaSocket, controlCode, &extensionGuid, DWORD{sizeof(extensionGuid)}, diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index d44badef..8113541c 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -2053,8 +2053,8 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_local_addresses") { - addr_info test_addr = wil::network::resolve_local_addresses_nothrow(); - REQUIRE(test_addr.get_last_error() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + const addr_info test_addr = wil::network::resolve_local_addresses(); REQUIRE(test_addr.begin() != test_addr.end()); for (const auto& address : test_addr) @@ -2065,32 +2065,15 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_local_addresses_nothrow : %ws\n", address_string); - } - -#ifdef WIL_ENABLE_EXCEPTIONS - { - test_addr = wil::network::resolve_local_addresses(); - REQUIRE(test_addr.get_last_error() == 0); - REQUIRE(test_addr.begin() != test_addr.end()); - - for (const auto& address : test_addr) - { - const auto family = address.family(); - REQUIRE((family == AF_INET || family == AF_INET6)); - REQUIRE(!address.is_address_loopback()); - - const auto address_string{address.write_address()}; - // wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); - } + // wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); } #endif } SECTION("verify resolve_localhost_addresses") { - addr_info test_addr = wil::network::resolve_localhost_addresses_nothrow(); - REQUIRE(test_addr.get_last_error() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + const addr_info test_addr = wil::network::resolve_localhost_addresses(); REQUIRE(test_addr.begin() != test_addr.end()); // verify operator-> REQUIRE(test_addr.begin()->is_address_loopback()); @@ -2117,48 +2100,15 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_localhost_addresses_nothrow : %ws\n", address_string); - } - -#ifdef WIL_ENABLE_EXCEPTIONS - { - test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(test_addr.get_last_error() == 0); - REQUIRE(test_addr.begin() != test_addr.end()); - // verify operator-> - REQUIRE(test_addr.begin()->is_address_loopback()); - - for (const auto& address : test_addr) - { - const auto family = address.family(); - REQUIRE((family == AF_INET || family == AF_INET6)); - REQUIRE(address.is_address_loopback()); - - switch (address.family()) - { - case AF_INET: - REQUIRE(equals(*address.in_addr(), Test_loopback_in_addr)); - break; - - case AF_INET6: - REQUIRE(equals(*address.in6_addr(), Test_loopback_in6_addr)); - break; - - default: - REQUIRE(false); - } - - const auto address_string{address.write_address()}; - // wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); - } + // wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); } #endif } SECTION("verify resolve_name") { - addr_info test_addr = wil::network::resolve_name_nothrow(L"..localmachine"); - REQUIRE(test_addr.get_last_error() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + const addr_info test_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(test_addr.begin() != test_addr.end()); for (const auto& address : test_addr) @@ -2169,31 +2119,15 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_name_nothrow(..localmachine) : %ws\n", address_string); - } - -#ifdef WIL_ENABLE_EXCEPTIONS - { - test_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(test_addr.get_last_error() == 0); - REQUIRE(test_addr.begin() != test_addr.end()); - - for (const auto& address : test_addr) - { - const auto family = address.family(); - REQUIRE((family == AF_INET || family == AF_INET6)); - REQUIRE(!address.is_address_loopback()); - - const auto address_string{address.write_address()}; - // wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); - } + // wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); } #endif } SECTION("verify const addr_info iterators") { - const addr_info test_addr = wil::network::resolve_name_nothrow(L"localhost"); +#ifdef WIL_ENABLE_EXCEPTIONS + const addr_info test_addr = wil::network::resolve_name(L"localhost"); REQUIRE(test_addr.begin() != test_addr.end()); const addr_info::iterator test_iterator = test_addr.begin(); @@ -2201,11 +2135,13 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") const auto& test_address_reference = *test_iterator; REQUIRE(test_address_reference.is_address_loopback()); +#endif } SECTION("verify addr_info iterator increment") { - const addr_info initial_addr = wil::network::resolve_name_nothrow(L"localhost"); +#ifdef WIL_ENABLE_EXCEPTIONS + const addr_info initial_addr = wil::network::resolve_name(L"localhost"); REQUIRE(initial_addr.begin() != initial_addr.end()); auto total_count = 0; @@ -2214,25 +2150,25 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") ++total_count; } - addr_info test_addr = wil::network::resolve_name_nothrow(L"localhost"); + addr_info test_addr = wil::network::resolve_name(L"localhost"); REQUIRE(initial_addr.begin() != initial_addr.end()); addr_info::iterator test_iterator = test_addr.begin(); test_iterator += total_count; REQUIRE(test_iterator == test_addr.end()); +#endif } SECTION("verify addr_info iterator move behavior") { - addr_info moved_from_addr = wil::network::resolve_name_nothrow(L"..localmachine"); - REQUIRE(moved_from_addr.get_last_error() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + addr_info moved_from_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); addr_info moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); - REQUIRE(moved_to_addr.get_last_error() == 0); REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); for (const auto& address : moved_to_addr) @@ -2243,23 +2179,22 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... moved resolve_name_nothrow(..localmachine) : %ws\n", address_string); + // wprintf(L"... moved resolve_name(..localmachine) : %ws\n", address_string); } +#endif } SECTION("verify addr_info iterator move assignment behavior") { - addr_info moved_from_addr = wil::network::resolve_name_nothrow(L"..localmachine"); - REQUIRE(moved_from_addr.get_last_error() == 0); +#ifdef WIL_ENABLE_EXCEPTIONS + addr_info moved_from_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); - addr_info moved_to_addr{wil::network::resolve_local_addresses_nothrow()}; + addr_info moved_to_addr{wil::network::resolve_local_addresses()}; moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); - - REQUIRE(moved_to_addr.get_last_error() == 0); REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); // move to self @@ -2275,19 +2210,16 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); } +#endif } SECTION("verify addr_info resolve_name failure") { - addr_info test_addr = wil::network::resolve_name_nothrow(L"...xyz.xyz..."); - REQUIRE(test_addr.get_last_error() == WSAHOST_NOT_FOUND); - REQUIRE(test_addr.begin() == test_addr.end()); - #ifdef WIL_ENABLE_EXCEPTIONS bool exception_thrown = false; try { - test_addr = wil::network::resolve_name(L"...xyz.xyz..."); + const auto test_addr = wil::network::resolve_name(L"...xyz.xyz..."); } catch (const wil::ResultException& e) { From 5c34e192981afda876b95b1e810bf974041feb9f Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 18 Jan 2025 17:56:46 -0800 Subject: [PATCH 17/22] pushing a new approach to objects managing the result from name resolution. (not yet complete) --- include/wil/network.h | 297 ++++++++++++++------------------------ include/wil/resource.h | 21 +++ tests/NetworkingTests.cpp | 85 ++++++----- 3 files changed, 183 insertions(+), 220 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 1d37157d..5974c066 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -46,7 +46,8 @@ #include #include -// the wil header for RAII types +// required wil headers +#include "wistd_type_traits.h" #include "resource.h" namespace wil @@ -351,11 +352,7 @@ namespace network void set_address(const IN_ADDR*) WI_NOEXCEPT; void set_address(const IN6_ADDR*) WI_NOEXCEPT; - // write_address prints the IP address portion, not the scope id or port -#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - [[nodiscard]] ::std::wstring write_address() const; - [[nodiscard]] ::std::wstring write_complete_address() const; -#endif + // write_address prints the IP address, not the scope id or port HRESULT write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; @@ -363,6 +360,11 @@ namespace network HRESULT write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; +#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) + [[nodiscard]] ::std::wstring write_address() const; + [[nodiscard]] ::std::wstring write_complete_address() const; +#endif + // type: NlatUnspecified ('any'), NlatUnicast, NlatAnycast, NlatMulticast, NlatBroadcast, NlatInvalid [[nodiscard]] NL_ADDRESS_TYPE address_type() const WI_NOEXCEPT; [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; @@ -423,204 +425,132 @@ namespace network //! class addr_info encapsulates the ADDRINFO structure //! this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo //! iterator semantics are supported to safely access these addresses - //! supports all addrinfo types: ADDRINFOA, ADDRINFOW, ADDRINFOEXA, ADDRINFOEXW -template - class addr_info_t - { - public: - addr_info_t(_In_ T* addrResult) WI_NOEXCEPT : m_addrResult{addrResult} - { - } + template + class addr_info_iterator_t; - // the d'tor calls the free function matching the addrinfo* type T - ~addr_info_t() WI_NOEXCEPT; - addr_info_t(const addr_info_t&) = delete; - addr_info_t& operator=(const addr_info_t&) = delete; + using addr_info_ansi_iterator = wil::network::addr_info_iterator_t; + using addr_info_iterator = wil::network::addr_info_iterator_t; + // not defining a type for ADDRINFOEXA as that type is formally __declspec(deprecated) + using addr_infoex_iterator = wil::network::addr_info_iterator_t; - addr_info_t(addr_info_t&& rhs) WI_NOEXCEPT + // template T supports all wil addrinfo types: ADDRINFOA, ADDRINFOW, ADDRINFOEXW, + template + class addr_info_iterator_t + { + public: + static addr_info_iterator_t begin(T* addrinfo_ptr) WI_NOEXCEPT { - m_addrResult = rhs.m_addrResult; - rhs.m_addrResult = nullptr; + return {addrinfo_ptr}; } - - addr_info_t& operator=(addr_info_t&& rhs) WI_NOEXCEPT + static addr_info_iterator_t end() WI_NOEXCEPT { - if (this != &rhs) - { - if (m_addrResult) - { - ::FreeAddrInfoW(m_addrResult); - } - m_addrResult = rhs.m_addrResult; - rhs.m_addrResult = nullptr; - } - - return *this; + return {nullptr}; } - class iterator - { - public: - // defining iterator_traits allows STL functions to be used with this iterator class. - // Notice this is a forward_iterator - // - does not support random-access (e.g. vector::iterator) - // - does not support bidirectional access (e.g. list::iterator) + // defining iterator_traits allows STL functions to be used with this iterator class. + // Notice this is a forward_iterator + // - does not support random-access (e.g. vector::iterator) + // - does not support bidirectional access (e.g. list::iterator) #if defined(_ITERATOR_) || defined(WIL_DOXYGEN) - using iterator_category = ::std::forward_iterator_tag; + using iterator_category = ::std::forward_iterator_tag; #endif - using value_type = wil::network::socket_address; - using difference_type = size_t; - using distance_type = size_t; - using pointer = wil::network::socket_address*; - using reference = wil::network::socket_address&; - - iterator(const ADDRINFOW* addrinfo) WI_NOEXCEPT : - m_addr_info(const_cast(addrinfo)) - { - // must const cast so we can re-use this pointer as we walk the list - if (m_addr_info) - { - m_socket_address.set_sockaddr(m_addr_info->ai_addr, m_addr_info->ai_addrlen); - } - } - - ~iterator() WI_NOEXCEPT = default; - iterator(const iterator&) WI_NOEXCEPT = default; - iterator& operator=(const iterator&) WI_NOEXCEPT = default; - iterator(iterator&&) WI_NOEXCEPT = default; - iterator& operator=(iterator&&) WI_NOEXCEPT = default; - - const wil::network::socket_address& operator*() const WI_NOEXCEPT - { - return m_socket_address; - } - - const wil::network::socket_address& operator*() WI_NOEXCEPT - { - return m_socket_address; - } - - const wil::network::socket_address* operator->() const WI_NOEXCEPT - { - return &m_socket_address; - } - - const wil::network::socket_address* operator->() WI_NOEXCEPT - { - return &m_socket_address; - } - - [[nodiscard]] bool operator==(const iterator& rhs) const WI_NOEXCEPT - { - return m_addr_info == rhs.m_addr_info; - } - - [[nodiscard]] bool operator!=(const iterator& rhs) const WI_NOEXCEPT - { - return !(*this == rhs); - } - - iterator& operator++() WI_NOEXCEPT - { - this->operator+=(1); - return *this; - } - - iterator operator++(int) WI_NOEXCEPT + using value_type = wil::network::socket_address; + using difference_type = size_t; + using distance_type = size_t; + using pointer = wil::network::socket_address*; + using reference = wil::network::socket_address&; + + addr_info_iterator_t(const T* addrinfo) WI_NOEXCEPT : + // must const cast so we can re-use this pointer as we walk the list + m_addrinfo_ptr(const_cast(addrinfo)) + { + if (m_addrinfo_ptr) { - auto return_value = *this; - this->operator+=(1); - return return_value; + m_socket_address.set_sockaddr(m_addrinfo_ptr->ai_addr, m_addrinfo_ptr->ai_addrlen); } + } - iterator& operator+=(size_t offset) WI_NOEXCEPT - { - for (size_t count = 0; count < offset; ++count) - { - WI_ASSERT(m_addr_info); - if (m_addr_info) - { - m_addr_info = m_addr_info->ai_next; - if (m_addr_info) - { - m_socket_address.set_sockaddr(m_addr_info->ai_addr, m_addr_info->ai_addrlen); - } - else - { - m_socket_address.reset(); - } - } - } + ~addr_info_iterator_t() WI_NOEXCEPT = default; + addr_info_iterator_t(const addr_info_iterator_t&) WI_NOEXCEPT = default; + addr_info_iterator_t& operator=(const addr_info_iterator_t&) WI_NOEXCEPT = default; + addr_info_iterator_t(addr_info_iterator_t&&) WI_NOEXCEPT = default; + addr_info_iterator_t& operator=(addr_info_iterator_t&&) WI_NOEXCEPT = default; - return *this; - } + const wil::network::socket_address& operator*() const WI_NOEXCEPT + { + return m_socket_address; + } - private: - // non-ownership of this pointer - the parent class must outlive the iterator - T* m_addr_info{nullptr}; - wil::network::socket_address m_socket_address{}; - }; + const wil::network::socket_address& operator*() WI_NOEXCEPT + { + return m_socket_address; + } - iterator begin() const WI_NOEXCEPT + const wil::network::socket_address* operator->() const WI_NOEXCEPT { - return {m_addrResult}; + return &m_socket_address; } - iterator end() const WI_NOEXCEPT + const wil::network::socket_address* operator->() WI_NOEXCEPT { - return {nullptr}; + return &m_socket_address; } - private: - T* m_addrResult{}; - }; + [[nodiscard]] bool operator==(const addr_info_iterator_t& rhs) const WI_NOEXCEPT + { + return m_addrinfo_ptr == rhs.m_addrinfo_ptr; + } - typedef network::addr_info_t addr_info_a; - template<> - inline addr_info_t::~addr_info_t() WI_NOEXCEPT - { - if (m_addrResult) + [[nodiscard]] bool operator!=(const addr_info_iterator_t& rhs) const WI_NOEXCEPT { - ::freeaddrinfo(m_addrResult); + return !(*this == rhs); } - } - typedef network::addr_info_t addr_info; - template<> - inline addr_info_t::~addr_info_t() WI_NOEXCEPT - { - if (m_addrResult) + + addr_info_iterator_t& operator++() WI_NOEXCEPT { - ::FreeAddrInfoW(m_addrResult); + this->operator+=(1); + return *this; } - } - // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API -#if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) - typedef network::addr_info_t addr_info_exa; - template<> - inline addr_info_t::~addr_info_t() WI_NOEXCEPT - { - if (m_addrResult) + + addr_info_iterator_t operator++(int) WI_NOEXCEPT { - ::FreeAddrInfoEx(m_addrResult); + auto return_value = *this; + this->operator+=(1); + return return_value; } - } -#endif - typedef network::addr_info_t addr_infoex; - template<> - inline addr_info_t::~addr_info_t() WI_NOEXCEPT - { - if (m_addrResult) + + addr_info_iterator_t& operator+=(size_t offset) WI_NOEXCEPT { - ::FreeAddrInfoExW(m_addrResult); + for (size_t count = 0; count < offset; ++count) + { + WI_ASSERT(m_addrinfo_ptr); + if (m_addrinfo_ptr) + { + m_addrinfo_ptr = m_addrinfo_ptr->ai_next; + if (m_addrinfo_ptr) + { + m_socket_address.set_sockaddr(m_addrinfo_ptr->ai_addr, m_addrinfo_ptr->ai_addrlen); + } + else + { + m_socket_address.reset(); + } + } + } + + return *this; } - } + private: + // non-ownership of this pointer - the parent class must outlive the iterator + T* m_addrinfo_ptr{nullptr}; + wil::network::socket_address m_socket_address{}; + }; // class addr_info_iterator_t #if defined(WIL_ENABLE_EXCEPTIONS) //! wil function to capture resolving a name to a set of IP addresses, throwing on error //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::network::addr_info resolve_name(_In_ PCWSTR name) + inline ::wil::unique_addrinfo resolve_name(_In_ PCWSTR name) { ADDRINFOW* addrResult{}; if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) @@ -633,16 +563,14 @@ template //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::network::addr_info resolve_local_addresses() WI_NOEXCEPT + inline ::wil::unique_addrinfo resolve_local_addresses() WI_NOEXCEPT { return ::wil::network::resolve_name(L""); } //! wil function to capture resolving the local-host addresses, throwing on error //! returning an RAII object containing the results - //! the returned RAII object exposes iterator semantics to walk the results - inline ::wil::network::addr_info resolve_localhost_addresses() WI_NOEXCEPT + inline ::wil::unique_addrinfo resolve_localhost_addresses() WI_NOEXCEPT { return ::wil::network::resolve_name(L"localhost"); } @@ -1121,9 +1049,10 @@ template return S_OK; } - const void* const pAddr{family() == AF_INET - ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; + const void* const pAddr{ + family() == AF_INET + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; // the last param to InetNtopW is # of characters, not bytes const auto* error_value{::InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN)}; @@ -1145,9 +1074,10 @@ template return S_OK; } - const void* const pAddr{family() == AF_INET - ? static_cast(&m_sockaddr.Ipv4.sin_addr) - : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; + const void* const pAddr{ + family() == AF_INET + ? static_cast(&m_sockaddr.Ipv4.sin_addr) + : static_cast(&m_sockaddr.Ipv6.sin6_addr)}; // the last param to InetNtopA is # of characters, not bytes const auto* error_value{::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN)}; @@ -1168,8 +1098,7 @@ template } #endif - inline HRESULT wil::network::socket_address::write_complete_address_nothrow( - socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT wil::network::socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); @@ -1209,11 +1138,7 @@ template DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringA( - const_cast(sockaddr()), - wil::network::socket_address::length, - nullptr, - address, - &addressLength) != 0) + const_cast(sockaddr()), wil::network::socket_address::length, nullptr, address, &addressLength) != 0) { RETURN_WIN32(::WSAGetLastError()); } diff --git a/include/wil/resource.h b/include/wil/resource.h index 32fcc586..2a5bb79b 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5081,6 +5081,27 @@ typedef shared_any shared_socket; typedef weak_any weak_socket; #endif // __WIL_WINSOCKAPI_STL +#if (defined _WS2TCPIP_H_) && !defined(__WIL_WS2TCPIP_H_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WS2TCPIP_H_ +/// @endcond +typedef unique_any unique_addrinfo_ansi; +typedef unique_any unique_addrinfo; +// not defining a type for FreeAddrInfoEx(PADDRINFOEXA) as that API is formally __declspec(deprecated) +typedef unique_any unique_addrinfoex; +#endif // __WIL_WS2TCPIP_H_ +#if (defined(__WIL_WS2TCPIP_H_) && !defined(__WIL_WS2TCPIP_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WS2TCPIP_H_STL +/// @endcond +typedef shared_any shared_addrinfo_ansi; +typedef weak_any weak_addrinfo_ansi; +typedef shared_any shared_addrinfo; +typedef weak_any weak_addrinfo; +typedef shared_any shared_addrinfoex; +typedef weak_any weak_addrinfoex; +#endif // __WIL_WS2TCPIP_H_STL + #if (defined(_WINGDI_) && !defined(__WIL_WINGDI_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(NOGDI) && !defined(WIL_KERNEL_MODE)) || \ defined(WIL_DOXYGEN) /// @cond diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 8113541c..6f579e16 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -2045,7 +2045,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { - using wil::network::addr_info; + using wil::network::addr_info_iterator; using wil::network::equals; const auto cleanup = wil::network::WSAStartup_nothrow(); @@ -2054,10 +2054,15 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_local_addresses") { #ifdef WIL_ENABLE_EXCEPTIONS - const addr_info test_addr = wil::network::resolve_local_addresses(); + const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); REQUIRE(test_addr.begin() != test_addr.end()); - for (const auto& address : test_addr) + // verify operator-> + const addr_info_iterator test_addr_iterator{test_addr.get()}; + REQUIRE(!test_addr_iterator->is_address_loopback()); + + uint32_t count = 0; + for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2065,20 +2070,26 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); + wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); + ++count; } + + REQUIRE(count > 0); #endif } SECTION("verify resolve_localhost_addresses") { #ifdef WIL_ENABLE_EXCEPTIONS - const addr_info test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(test_addr.begin() != test_addr.end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); + REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + // verify operator-> - REQUIRE(test_addr.begin()->is_address_loopback()); + const addr_info_iterator test_addr_iterator{test_addr.get()}; + REQUIRE(test_addr_iterator->is_address_loopback()); - for (const auto& address : test_addr) + uint32_t count = 0; + for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2100,18 +2111,22 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); + wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); + ++count; } + + REQUIRE(count > 0); #endif } SECTION("verify resolve_name") { #ifdef WIL_ENABLE_EXCEPTIONS - const addr_info test_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(test_addr.begin() != test_addr.end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"..localmachine"); + REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); - for (const auto& address : test_addr) + uint32_t count = 0; + for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2119,19 +2134,22 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); + wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); + ++count; } + + REQUIRE(count > 0); #endif } SECTION("verify const addr_info iterators") { #ifdef WIL_ENABLE_EXCEPTIONS - const addr_info test_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(test_addr.begin() != test_addr.end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"localhost"); + REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); - const addr_info::iterator test_iterator = test_addr.begin(); - REQUIRE(test_iterator->is_address_loopback()); + const addr_info_iterator test_addr_iterator{test_addr.get()}; + REQUIRE(test_addr_iterator->is_address_loopback()); const auto& test_address_reference = *test_iterator; REQUIRE(test_address_reference.is_address_loopback()); @@ -2142,36 +2160,35 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { #ifdef WIL_ENABLE_EXCEPTIONS const addr_info initial_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(initial_addr.begin() != initial_addr.end()); + REQUIRE(addr_info_iterator::begin(initial_addr.get()) != addr_info_iterator::end()); auto total_count = 0; - for (auto it = initial_addr.begin(); it != initial_addr.end(); ++it) + for (auto it = addr_info_iterator::begin(initial_addr.get()); it != addr_info_iterator::end(); ++it) { ++total_count; } - addr_info test_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(initial_addr.begin() != initial_addr.end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"localhost"); + REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); - addr_info::iterator test_iterator = test_addr.begin(); + addr_info_iterator test_iterator = addr_info_iterator::begin(test_addr.get()); test_iterator += total_count; - REQUIRE(test_iterator == test_addr.end()); + REQUIRE(test_iterator == addr_info_iterator::end()); #endif } SECTION("verify addr_info iterator move behavior") { #ifdef WIL_ENABLE_EXCEPTIONS - addr_info moved_from_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); + wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); + REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); - addr_info moved_to_addr = std::move(moved_from_addr); + const wil::unique_addrinfo moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now - REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); + REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) == addr_info_iterator::end()); + REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); - REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); - - for (const auto& address : moved_to_addr) + for (const auto& address : wil::make_range(addr_info_iterator::begin(moved_to_addr.get()), addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2179,7 +2196,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - // wprintf(L"... moved resolve_name(..localmachine) : %ws\n", address_string); + wprintf(L"... moved resolve_name(..localmachine) : %ws\n", address_string); } #endif } @@ -2187,10 +2204,10 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info iterator move assignment behavior") { #ifdef WIL_ENABLE_EXCEPTIONS - addr_info moved_from_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(moved_from_addr.begin() != moved_from_addr.end()); + wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); + REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); - addr_info moved_to_addr{wil::network::resolve_local_addresses()}; + wil::unique_addrinfo moved_to_addr{wil::network::resolve_local_addresses()}; moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now From f4d9c872d071b397104d3de4955fb1ca1bc43a60 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sun, 19 Jan 2025 01:57:10 -0800 Subject: [PATCH 18/22] More updates - more consolidation. Making addr_info ranges. --- include/wil/network.h | 554 ++++++++++++++++++-------------------- include/wil/resource.h | 2 +- tests/NetworkingTests.cpp | 64 ++--- 3 files changed, 302 insertions(+), 318 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 5974c066..a1b92394 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -109,11 +109,17 @@ namespace network LPFN_WSASENDMSG WSASendMsg{nullptr}; }; - struct winsock_extension_function_table + template + struct socket_extension_function_table_t; + using winsock_extension_function_table = socket_extension_function_table_t; + using rio_extension_function_table = socket_extension_function_table_t; + + template + struct socket_extension_function_table_t { - static winsock_extension_function_table load() WI_NOEXCEPT; + static socket_extension_function_table_t load() WI_NOEXCEPT; - ~winsock_extension_function_table() WI_NOEXCEPT = default; + ~socket_extension_function_table_t() WI_NOEXCEPT = default; // can copy, but the new object needs its own WSA reference count // (getting a WSA reference count should be no-fail once the first reference it taken) @@ -124,7 +130,7 @@ namespace network // (this should never happen the caller has a reference, but we failed to get a WSA reference) // THEN // this object cannot carry forward any function pointers - it must show successfully loaded == false - winsock_extension_function_table(const winsock_extension_function_table& rhs) WI_NOEXCEPT : + socket_extension_function_table_t(const socket_extension_function_table_t& rhs) WI_NOEXCEPT : wsa_reference_count{WSAStartup_nothrow()} { if (!wsa_reference_count || !rhs.wsa_reference_count) @@ -137,7 +143,7 @@ namespace network } } - winsock_extension_function_table& operator=(const winsock_extension_function_table& rhs) WI_NOEXCEPT + socket_extension_function_table_t& operator=(const socket_extension_function_table_t& rhs) WI_NOEXCEPT { if (!wsa_reference_count || !rhs.wsa_reference_count) { @@ -152,16 +158,13 @@ namespace network } //! Returns true if all functions were loaded, holding a WSAStartup reference - WI_NODISCARD explicit operator bool() const WI_NOEXCEPT - { - return f.AcceptEx != nullptr; - } + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT; - ::wil::network::WINSOCK_EXTENSION_FUNCTION_TABLE f{}; + F f{}; private: // constructed via load() - winsock_extension_function_table() WI_NOEXCEPT : + socket_extension_function_table_t() WI_NOEXCEPT : wsa_reference_count{WSAStartup_nothrow()} { } @@ -170,66 +173,141 @@ namespace network const ::wil::network::unique_wsacleanup_call wsa_reference_count; }; - struct rio_extension_function_table + // + // explicit specializations for socket_extension_function_table_t + // + template <> + inline + socket_extension_function_table_t::operator bool() const WI_NOEXCEPT { - static rio_extension_function_table load() WI_NOEXCEPT; + return f.AcceptEx != nullptr; + } - ~rio_extension_function_table() WI_NOEXCEPT = default; + template <> + inline + socket_extension_function_table_t::operator bool() const WI_NOEXCEPT + { + return f.RIOReceive != nullptr; + } - // can copy, but the new object needs its own WSA reference count - // (getting a WSA reference count should be no-fail once the first reference it taken) - // - // IF the target could not get a WSA reference count - // OR - // IF we couldn't get our own WSA reference count - // (this should never happen the caller has a reference, but we failed to get a WSA reference) - // THEN - // this object cannot carry forward any function pointers - it must show successfully loaded == false - rio_extension_function_table(const rio_extension_function_table& rhs) WI_NOEXCEPT : - wsa_reference_count{WSAStartup_nothrow()} + template <> + inline + winsock_extension_function_table socket_extension_function_table_t::load() WI_NOEXCEPT + { + winsock_extension_function_table table; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) { - if (!wsa_reference_count || !rhs.wsa_reference_count) - { - ::memset(&f, 0, sizeof(f)); - } - else - { - ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); - } + return table; } - rio_extension_function_table& operator=(const rio_extension_function_table& rhs) WI_NOEXCEPT + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) { - if (!wsa_reference_count || !rhs.wsa_reference_count) - { - ::memset(&f, 0, sizeof(f)); - } - else - { - ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); - } + return table; + } - return *this; + const auto load_function_pointer = [](SOCKET lambdaSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { + constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(functionPtr)}; + DWORD unused_bytes{}; + const auto error{::WSAIoctl( + lambdaSocket, + controlCode, + &extensionGuid, + DWORD{sizeof(extensionGuid)}, + functionPtr, + bytes, + &unused_bytes, + nullptr, + nullptr)}; + return error == 0 ? S_OK : HRESULT_FROM_WIN32(::WSAGetLastError()); + }; + + if (FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_ACCEPTEX, &table.f.AcceptEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_CONNECTEX, &table.f.ConnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_DISCONNECTEX, &table.f.DisconnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_GETACCEPTEXSOCKADDRS, &table.f.GetAcceptExSockaddrs)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITFILE, &table.f.TransmitFile)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITPACKETS, &table.f.TransmitPackets)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSARECVMSG, &table.f.WSARecvMsg)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSASENDMSG, &table.f.WSASendMsg))) + { + // if any failed to be found, something is very broken + // all should load, or all should fail + ::memset(&table.f, 0, sizeof(table.f)); } - //! Returns true if all functions were loaded, holding a WSAStartup reference - WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + return table; + } + + template <> + inline + rio_extension_function_table socket_extension_function_table_t::load() WI_NOEXCEPT + { + rio_extension_function_table table{}; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) { - return f.RIOReceive != nullptr; + return table; } - ::RIO_EXTENSION_FUNCTION_TABLE f{}; + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + return table; + } - private: - // constructed via load() - rio_extension_function_table() WI_NOEXCEPT : - wsa_reference_count{WSAStartup_nothrow()} + constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(table.f)}; + GUID rioGuid = WSAID_MULTIPLE_RIO; + + ::memset(&table.f, 0, bytes); + table.f.cbSize = bytes; + + DWORD unused_bytes{}; + if (::WSAIoctl( + localSocket.get(), + controlCode, + &rioGuid, + DWORD{sizeof(rioGuid)}, + &table.f, + bytes, + &unused_bytes, + nullptr, + nullptr) != 0) { + LOG_IF_WIN32_ERROR(::WSAGetLastError()); + + ::memset(&table.f, 0, bytes); } + return table; + } - // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const ::wil::network::unique_wsacleanup_call wsa_reference_count; - }; + // + // utility functions to compare inaddr types + // + [[nodiscard]] inline bool equals(const ::in_addr& lhs, const ::in_addr& rhs) WI_NOEXCEPT + { + return lhs.s_addr == rhs.s_addr; + } + + [[nodiscard]] inline bool not_equals(const ::in_addr& lhs, const ::in_addr& rhs) WI_NOEXCEPT + { + return !equals(lhs, rhs); + } + + [[nodiscard]] inline bool equals(const ::in6_addr& lhs, const ::in6_addr& rhs) WI_NOEXCEPT + { + return 0 == ::memcmp(&lhs, &rhs, sizeof(::in6_addr)); + } + + [[nodiscard]] inline bool not_equals(const ::in6_addr& lhs, const ::in6_addr& rhs) WI_NOEXCEPT + { + return !equals(lhs, rhs); + } // //! encapsulates working with the sockaddr datatype @@ -267,26 +345,6 @@ namespace network //! - a structure containing both a sockaddr* and its length fields, returned from some networking functions //! - [[nodiscard]] inline bool equals(const in_addr& lhs, const in_addr& rhs) WI_NOEXCEPT - { - return lhs.s_addr == rhs.s_addr; - } - - [[nodiscard]] inline bool not_equals(const in_addr& lhs, const in_addr& rhs) WI_NOEXCEPT - { - return lhs.s_addr != rhs.s_addr; - } - - [[nodiscard]] inline bool equals(const in6_addr& lhs, const in6_addr& rhs) WI_NOEXCEPT - { - return 0 == ::memcmp(&lhs, &rhs, sizeof(in6_addr)); - } - - [[nodiscard]] inline bool not_equals(const in6_addr& lhs, const in6_addr& rhs) WI_NOEXCEPT - { - return 0 != ::memcmp(&lhs, &rhs, sizeof(in6_addr)); - } - // declaring char-arrays large enough for any IPv4 or IPv6 address + optional fields static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN); typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; @@ -297,7 +355,7 @@ namespace network public: explicit socket_address(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; template - explicit socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT; + explicit socket_address(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT; explicit socket_address(const SOCKADDR_IN*) WI_NOEXCEPT; explicit socket_address(const SOCKADDR_IN6*) WI_NOEXCEPT; explicit socket_address(const SOCKADDR_INET*) WI_NOEXCEPT; @@ -322,9 +380,9 @@ namespace network void swap(socket_address&) WI_NOEXCEPT; void reset(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; - // set_sockaddr overwrites the entire sockaddr in the object + // set_sockaddr overwrites the entire sockaddr in the object (including address family) template - void set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT; + void set_sockaddr(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT; void set_sockaddr(const SOCKADDR_IN*) WI_NOEXCEPT; void set_sockaddr(const SOCKADDR_IN6*) WI_NOEXCEPT; void set_sockaddr(const SOCKADDR_INET*) WI_NOEXCEPT; @@ -338,13 +396,9 @@ namespace network [[nodiscard]] HRESULT set_sockaddr_nothrow(PCWSTR) WI_NOEXCEPT; [[nodiscard]] HRESULT set_sockaddr_nothrow(PCSTR) WI_NOEXCEPT; - void set_port(USHORT) WI_NOEXCEPT; - void set_scope_id(ULONG) WI_NOEXCEPT; - void set_flow_info(ULONG) WI_NOEXCEPT; - // set_address* preserves the existing port set on the address // - so that one can call set_port() and set_address() in any order with expected results - // - the address family is preserved unless it is specified (or inferred) as an argument + // the address family is preserved unless it is specified (or inferred) as an argument void set_address_any() WI_NOEXCEPT; void set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT; void set_address_loopback() WI_NOEXCEPT; @@ -352,6 +406,10 @@ namespace network void set_address(const IN_ADDR*) WI_NOEXCEPT; void set_address(const IN6_ADDR*) WI_NOEXCEPT; + void set_port(USHORT) WI_NOEXCEPT; + void set_scope_id(ULONG) WI_NOEXCEPT; + void set_flow_info(ULONG) WI_NOEXCEPT; + // write_address prints the IP address, not the scope id or port HRESULT write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; @@ -370,7 +428,6 @@ namespace network [[nodiscard]] bool is_address_linklocal() const WI_NOEXCEPT; [[nodiscard]] bool is_address_loopback() const WI_NOEXCEPT; - // Accessors [[nodiscard]] ADDRESS_FAMILY family() const WI_NOEXCEPT; [[nodiscard]] USHORT port() const WI_NOEXCEPT; [[nodiscard]] ULONG flow_info() const WI_NOEXCEPT; @@ -390,7 +447,10 @@ namespace network [[nodiscard]] const IN_ADDR* in_addr() const WI_NOEXCEPT; [[nodiscard]] const IN6_ADDR* in6_addr() const WI_NOEXCEPT; - static constexpr int length{sizeof(::SOCKADDR_INET)}; + constexpr int length() const + { + return sizeof(m_sockaddr); + } private: SOCKADDR_INET m_sockaddr{}; @@ -398,26 +458,26 @@ namespace network // When using dual-mode sockets, one might need to connect to a target IPv4 address. // Since dual-mode socket types are IPv6, one must map that IPv4 address to its 'mapped' IPv6 address - inline wil::network::socket_address map_dual_mode_4to6(const wil::network::socket_address& inV4) WI_NOEXCEPT + inline ::wil::network::socket_address map_dual_mode_4to6(const ::wil::network::socket_address& ipv4_address) WI_NOEXCEPT { - constexpr IN6_ADDR v4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; + constexpr IN6_ADDR ipv4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; - wil::network::socket_address outV6{&v4MappedPrefix, inV4.port()}; + ::wil::network::socket_address return_ipv6_address{&ipv4MappedPrefix, ipv4_address.port()}; - auto* const pIn6Addr{outV6.in6_addr()}; - const auto* const pIn4Addr{inV4.in_addr()}; - pIn6Addr->u.Byte[12] = pIn4Addr->S_un.S_un_b.s_b1; - pIn6Addr->u.Byte[13] = pIn4Addr->S_un.S_un_b.s_b2; - pIn6Addr->u.Byte[14] = pIn4Addr->S_un.S_un_b.s_b3; - pIn6Addr->u.Byte[15] = pIn4Addr->S_un.S_un_b.s_b4; + auto* const return_in6_addr{return_ipv6_address.in6_addr()}; + const auto* const ipv4_inaddr{ipv4_address.in_addr()}; + return_in6_addr->u.Byte[12] = ipv4_inaddr->S_un.S_un_b.s_b1; + return_in6_addr->u.Byte[13] = ipv4_inaddr->S_un.S_un_b.s_b2; + return_in6_addr->u.Byte[14] = ipv4_inaddr->S_un.S_un_b.s_b3; + return_in6_addr->u.Byte[15] = ipv4_inaddr->S_un.S_un_b.s_b4; - return outV6; + return return_ipv6_address; } // // non-member swap // - inline void swap(wil::network::socket_address& lhs, wil::network::socket_address& rhs) WI_NOEXCEPT + inline void swap(::wil::network::socket_address& lhs, ::wil::network::socket_address& rhs) WI_NOEXCEPT { lhs.swap(rhs); } @@ -428,23 +488,24 @@ namespace network template class addr_info_iterator_t; - using addr_info_ansi_iterator = wil::network::addr_info_iterator_t; - using addr_info_iterator = wil::network::addr_info_iterator_t; + using addr_info_ansi_iterator = addr_info_iterator_t; + using addr_info_iterator = addr_info_iterator_t; // not defining a type for ADDRINFOEXA as that type is formally __declspec(deprecated) - using addr_infoex_iterator = wil::network::addr_info_iterator_t; + using addr_infoex_iterator = addr_info_iterator_t; // template T supports all wil addrinfo types: ADDRINFOA, ADDRINFOW, ADDRINFOEXW, template class addr_info_iterator_t { public: - static addr_info_iterator_t begin(T* addrinfo_ptr) WI_NOEXCEPT + static addr_info_iterator_t begin(const T* addrinfo_ptr) WI_NOEXCEPT { - return {addrinfo_ptr}; + return addr_info_iterator_t{addrinfo_ptr}; } + static addr_info_iterator_t end() WI_NOEXCEPT { - return {nullptr}; + return addr_info_iterator_t{nullptr}; } // defining iterator_traits allows STL functions to be used with this iterator class. @@ -454,13 +515,17 @@ namespace network #if defined(_ITERATOR_) || defined(WIL_DOXYGEN) using iterator_category = ::std::forward_iterator_tag; #endif - using value_type = wil::network::socket_address; + using value_type = ::wil::network::socket_address; using difference_type = size_t; using distance_type = size_t; - using pointer = wil::network::socket_address*; - using reference = wil::network::socket_address&; + using pointer = ::wil::network::socket_address*; + using reference = ::wil::network::socket_address&; - addr_info_iterator_t(const T* addrinfo) WI_NOEXCEPT : + explicit addr_info_iterator_t(nullptr_t) WI_NOEXCEPT + { + } + + explicit addr_info_iterator_t(const T* addrinfo) WI_NOEXCEPT : // must const cast so we can re-use this pointer as we walk the list m_addrinfo_ptr(const_cast(addrinfo)) { @@ -476,22 +541,22 @@ namespace network addr_info_iterator_t(addr_info_iterator_t&&) WI_NOEXCEPT = default; addr_info_iterator_t& operator=(addr_info_iterator_t&&) WI_NOEXCEPT = default; - const wil::network::socket_address& operator*() const WI_NOEXCEPT + const ::wil::network::socket_address& operator*() const WI_NOEXCEPT { return m_socket_address; } - const wil::network::socket_address& operator*() WI_NOEXCEPT + const ::wil::network::socket_address& operator*() WI_NOEXCEPT { return m_socket_address; } - const wil::network::socket_address* operator->() const WI_NOEXCEPT + const ::wil::network::socket_address* operator->() const WI_NOEXCEPT { return &m_socket_address; } - const wil::network::socket_address* operator->() WI_NOEXCEPT + const ::wil::network::socket_address* operator->() WI_NOEXCEPT { return &m_socket_address; } @@ -527,6 +592,7 @@ namespace network if (m_addrinfo_ptr) { m_addrinfo_ptr = m_addrinfo_ptr->ai_next; + if (m_addrinfo_ptr) { m_socket_address.set_sockaddr(m_addrinfo_ptr->ai_addr, m_addrinfo_ptr->ai_addrlen); @@ -544,21 +610,36 @@ namespace network private: // non-ownership of this pointer - the parent class must outlive the iterator T* m_addrinfo_ptr{nullptr}; - wil::network::socket_address m_socket_address{}; + ::wil::network::socket_address m_socket_address{}; }; // class addr_info_iterator_t + inline auto make_range(const ::wil::unique_addrinfo_ansi& addrinfo) + { + return ::wil::make_range(addr_info_ansi_iterator{addrinfo.get()}, addr_info_ansi_iterator{nullptr}); + } + + inline auto make_range(const ::wil::unique_addrinfo& addrinfo) + { + return ::wil::make_range(addr_info_iterator{addrinfo.get()}, addr_info_iterator{nullptr}); + } + + inline auto make_range(const ::wil::unique_addrinfoex& addrinfo) + { + return ::wil::make_range(addr_infoex_iterator{addrinfo.get()}, addr_infoex_iterator{nullptr}); + } + #if defined(WIL_ENABLE_EXCEPTIONS) //! wil function to capture resolving a name to a set of IP addresses, throwing on error //! returning an RAII object containing the results inline ::wil::unique_addrinfo resolve_name(_In_ PCWSTR name) { ADDRINFOW* addrResult{}; - if (0 != ::GetAddrInfoW(name, nullptr, nullptr, &addrResult)) + if (::GetAddrInfoW(name, nullptr, nullptr, &addrResult) != 0) { THROW_WIN32(::WSAGetLastError()); } - return {addrResult}; + return ::wil::unique_addrinfo{addrResult}; } //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error @@ -579,45 +660,45 @@ namespace network // // socket_address definitions // - inline wil::network::socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT + inline socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT { reset(family); } template - wil::network::socket_address::socket_address(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + socket_address::socket_address(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT { - set_sockaddr(addr, inLength); + set_sockaddr(addr, addr_size); } - inline wil::network::socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline socket_address::socket_address(const SOCKADDR_IN* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline wil::network::socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline socket_address::socket_address(const SOCKADDR_IN6* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline wil::network::socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline socket_address::socket_address(const SOCKADDR_INET* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline wil::network::socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline socket_address::socket_address(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { set_sockaddr(addr); } - inline wil::network::socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT { reset(AF_INET); set_address(addr); set_port(port); } - inline wil::network::socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT + inline socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT { reset(AF_INET6); set_address(addr); @@ -625,13 +706,13 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) - inline wil::network::socket_address::socket_address(PCWSTR addr, unsigned short port) + inline socket_address::socket_address(PCWSTR addr, unsigned short port) { set_sockaddr(addr); set_port(port); } #endif - inline bool wil::network::socket_address::operator==(const wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool socket_address::operator==(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { const auto& lhs{*this}; @@ -650,12 +731,12 @@ namespace network return ::memcmp(&lhs.m_sockaddr.Ipv6, &rhs.m_sockaddr.Ipv6, sizeof(SOCKADDR_IN6)) == 0; } - inline bool wil::network::socket_address::operator!=(const wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool socket_address::operator!=(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this == rhs); } - inline bool wil::network::socket_address::operator<(const wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool socket_address::operator<(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { const auto& lhs{*this}; @@ -734,12 +815,12 @@ namespace network } } - inline bool wil::network::socket_address::operator>(const wil::network::socket_address& rhs) const WI_NOEXCEPT + inline bool socket_address::operator>(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { return !(*this < rhs); } - inline void wil::network::socket_address::swap(socket_address& addr) WI_NOEXCEPT + inline void socket_address::swap(socket_address& addr) WI_NOEXCEPT { SOCKADDR_INET tempAddr{}; ::memcpy_s(&tempAddr, sizeof(tempAddr), &addr.m_sockaddr, sizeof(addr.m_sockaddr)); @@ -747,60 +828,60 @@ namespace network ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), &tempAddr, sizeof(tempAddr)); } - inline void wil::network::socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void socket_address::reset(ADDRESS_FAMILY family) WI_NOEXCEPT { -#ifndef WI_NETWORK_TEST +#if (!defined(WI_NETWORK_TEST)) WI_ASSERT(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); #endif - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); m_sockaddr.si_family = family; } template - void wil::network::socket_address::set_sockaddr(_In_reads_bytes_(inLength) const SOCKADDR* addr, T inLength) WI_NOEXCEPT + void socket_address::set_sockaddr(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT { - WI_ASSERT(static_cast(inLength) <= wil::network::socket_address::length); + WI_ASSERT(static_cast(addr_size) <= static_cast(length())); - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); if (addr) { - ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, inLength); + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, addr_size); } } - inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT + inline void socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); ::memcpy_s(&m_sockaddr.Ipv4, sizeof(m_sockaddr.Ipv4), addr, sizeof(*addr)); } - inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT + inline void socket_address::set_sockaddr(const SOCKADDR_IN6* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); ::memcpy_s(&m_sockaddr.Ipv6, sizeof(m_sockaddr.Ipv6), addr, sizeof(*addr)); } - inline void wil::network::socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT + inline void socket_address::set_sockaddr(const SOCKADDR_INET* addr) WI_NOEXCEPT { - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, sizeof(*addr)); } - inline void wil::network::socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT + inline void socket_address::set_sockaddr(const SOCKET_ADDRESS* addr) WI_NOEXCEPT { FAIL_FAST_IF_MSG( - addr->lpSockaddr && addr->iSockaddrLength > wil::network::socket_address::length, + addr->lpSockaddr && addr->iSockaddrLength > length(), "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", addr->iSockaddrLength); - ::memset(&m_sockaddr, 0, wil::network::socket_address::length); + ::memset(&m_sockaddr, 0, length()); if (addr->lpSockaddr) { ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr->lpSockaddr, addr->iSockaddrLength); } } - inline bool wil::network::socket_address::is_address_linklocal() const WI_NOEXCEPT + inline bool socket_address::is_address_linklocal() const WI_NOEXCEPT { switch (family()) { @@ -819,7 +900,7 @@ namespace network } } - inline bool wil::network::socket_address::is_address_loopback() const WI_NOEXCEPT + inline bool socket_address::is_address_loopback() const WI_NOEXCEPT { switch (family()) { @@ -838,7 +919,7 @@ namespace network } } - inline NL_ADDRESS_TYPE wil::network::socket_address::address_type() const WI_NOEXCEPT + inline NL_ADDRESS_TYPE socket_address::address_type() const WI_NOEXCEPT { switch (family()) { @@ -857,7 +938,7 @@ namespace network } } - inline void wil::network::socket_address::set_port(USHORT port) WI_NOEXCEPT + inline void socket_address::set_port(USHORT port) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET || family() == AF_INET6); @@ -868,7 +949,7 @@ namespace network m_sockaddr.Ipv4.sin_port = ::htons(port); } - inline void wil::network::socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT + inline void socket_address::set_scope_id(ULONG scopeId) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -878,7 +959,7 @@ namespace network } } - inline void wil::network::socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT + inline void socket_address::set_flow_info(ULONG flowInfo) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -888,12 +969,12 @@ namespace network } } - inline void wil::network::socket_address::set_address_any() WI_NOEXCEPT + inline void socket_address::set_address_any() WI_NOEXCEPT { set_address_any(family()); } - inline void wil::network::socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void socket_address::set_address_any(ADDRESS_FAMILY family) WI_NOEXCEPT { WI_ASSERT(family == AF_INET || family == AF_INET6); @@ -905,12 +986,12 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void wil::network::socket_address::set_address_loopback() WI_NOEXCEPT + inline void socket_address::set_address_loopback() WI_NOEXCEPT { set_address_loopback(family()); } - inline void wil::network::socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT + inline void socket_address::set_address_loopback(ADDRESS_FAMILY family) WI_NOEXCEPT { // the port value is at the exact same offset with both the IPv4 and IPv6 unions static_assert(FIELD_OFFSET(SOCKADDR_INET, Ipv4.sin_port) == FIELD_OFFSET(SOCKADDR_INET, Ipv6.sin6_port)); @@ -931,7 +1012,7 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void wil::network::socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT + inline void socket_address::set_address(const IN_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); @@ -941,7 +1022,7 @@ namespace network m_sockaddr.Ipv4.sin_port = original_port; } - inline void wil::network::socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT + inline void socket_address::set_address(const IN6_ADDR* addr) WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); @@ -952,27 +1033,27 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) - inline void wil::network::socket_address::set_sockaddr(SOCKET s) + inline void socket_address::set_sockaddr(SOCKET s) { THROW_IF_FAILED(set_sockaddr_nothrow(s)); } - inline void wil::network::socket_address::set_sockaddr(PCWSTR address) + inline void socket_address::set_sockaddr(PCWSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } - inline void wil::network::socket_address::set_sockaddr(PCSTR address) + inline void socket_address::set_sockaddr(PCSTR address) { THROW_IF_FAILED(set_sockaddr_nothrow(address)); } #endif - inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT + inline HRESULT socket_address::set_sockaddr_nothrow(SOCKET s) WI_NOEXCEPT { reset(AF_UNSPEC); - auto nameLength{wil::network::socket_address::length}; + auto nameLength{length()}; auto error{::getsockname(s, sockaddr(), &nameLength)}; if (error != 0) { @@ -982,7 +1063,7 @@ namespace network return S_OK; } - inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT + inline HRESULT socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT { PCWSTR terminator_unused; @@ -1005,7 +1086,7 @@ namespace network return E_INVALIDARG; } - inline HRESULT wil::network::socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT + inline HRESULT socket_address::set_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT { PCSTR terminator_unused; @@ -1029,16 +1110,16 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline ::std::wstring wil::network::socket_address::write_address() const + inline ::std::wstring socket_address::write_address() const { - wil::network::socket_address_wstring returnString{}; + ::wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT wil::network::socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT socket_address::write_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); @@ -1063,7 +1144,7 @@ namespace network return S_OK; } - inline HRESULT wil::network::socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT socket_address::write_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); @@ -1089,16 +1170,16 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) - inline ::std::wstring wil::network::socket_address::write_complete_address() const + inline ::std::wstring socket_address::write_complete_address() const { - wil::network::socket_address_wstring returnString{}; + ::wil::network::socket_address_wstring returnString{}; THROW_IF_FAILED(write_complete_address_nothrow(returnString)); returnString[INET6_ADDRSTRLEN - 1] = L'\0'; return returnString; } #endif - inline HRESULT wil::network::socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT + inline HRESULT socket_address::write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_wstring)); @@ -1113,7 +1194,7 @@ namespace network DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringW( const_cast(sockaddr()), - wil::network::socket_address::length, + length(), nullptr, address, &addressLength) != 0) @@ -1125,7 +1206,7 @@ namespace network // the Winsock headers require having set this #define to access ANSI-string versions of the Winsock API #if defined(_WINSOCK_DEPRECATED_NO_WARNINGS) - inline HRESULT wil::network::socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT + inline HRESULT socket_address::write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT { ::memset(address, 0, sizeof(socket_address_string)); @@ -1138,7 +1219,7 @@ namespace network DWORD addressLength{INET6_ADDRSTRLEN}; if (::WSAAddressToStringA( - const_cast(sockaddr()), wil::network::socket_address::length, nullptr, address, &addressLength) != 0) + const_cast(sockaddr()), length(), nullptr, address, &addressLength) != 0) { RETURN_WIN32(::WSAGetLastError()); } @@ -1146,12 +1227,12 @@ namespace network } #endif - inline ADDRESS_FAMILY wil::network::socket_address::family() const WI_NOEXCEPT + inline ADDRESS_FAMILY socket_address::family() const WI_NOEXCEPT { return m_sockaddr.si_family; } - inline USHORT wil::network::socket_address::port() const WI_NOEXCEPT + inline USHORT socket_address::port() const WI_NOEXCEPT { switch (family()) { @@ -1167,7 +1248,7 @@ namespace network } } - inline ULONG wil::network::socket_address::flow_info() const WI_NOEXCEPT + inline ULONG socket_address::flow_info() const WI_NOEXCEPT { switch (family()) { @@ -1183,7 +1264,7 @@ namespace network } } - inline ULONG wil::network::socket_address::scope_id() const WI_NOEXCEPT + inline ULONG socket_address::scope_id() const WI_NOEXCEPT { switch (family()) { @@ -1199,170 +1280,73 @@ namespace network } } - inline SOCKADDR* wil::network::socket_address::sockaddr() WI_NOEXCEPT + inline SOCKADDR* socket_address::sockaddr() WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline SOCKADDR_IN* wil::network::socket_address::sockaddr_in() WI_NOEXCEPT + inline SOCKADDR_IN* socket_address::sockaddr_in() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline SOCKADDR_IN6* wil::network::socket_address::sockaddr_in6() WI_NOEXCEPT + inline SOCKADDR_IN6* socket_address::sockaddr_in6() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline SOCKADDR_INET* wil::network::socket_address::sockaddr_inet() WI_NOEXCEPT + inline SOCKADDR_INET* socket_address::sockaddr_inet() WI_NOEXCEPT { return &m_sockaddr; } - inline IN_ADDR* wil::network::socket_address::in_addr() WI_NOEXCEPT + inline IN_ADDR* socket_address::in_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline IN6_ADDR* wil::network::socket_address::in6_addr() WI_NOEXCEPT + inline IN6_ADDR* socket_address::in6_addr() WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } - inline const SOCKADDR* wil::network::socket_address::sockaddr() const WI_NOEXCEPT + inline const SOCKADDR* socket_address::sockaddr() const WI_NOEXCEPT { return reinterpret_cast(&m_sockaddr); } - inline const SOCKADDR_IN* wil::network::socket_address::sockaddr_in() const WI_NOEXCEPT + inline const SOCKADDR_IN* socket_address::sockaddr_in() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4; } - inline const SOCKADDR_IN6* wil::network::socket_address::sockaddr_in6() const WI_NOEXCEPT + inline const SOCKADDR_IN6* socket_address::sockaddr_in6() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6; } - inline const SOCKADDR_INET* wil::network::socket_address::sockaddr_inet() const WI_NOEXCEPT + inline const SOCKADDR_INET* socket_address::sockaddr_inet() const WI_NOEXCEPT { return &m_sockaddr; } - inline const IN_ADDR* wil::network::socket_address::in_addr() const WI_NOEXCEPT + inline const IN_ADDR* socket_address::in_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET); return &m_sockaddr.Ipv4.sin_addr; } - inline const IN6_ADDR* wil::network::socket_address::in6_addr() const WI_NOEXCEPT + inline const IN6_ADDR* socket_address::in6_addr() const WI_NOEXCEPT { WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } - - inline winsock_extension_function_table winsock_extension_function_table::load() WI_NOEXCEPT - { - winsock_extension_function_table table; - // if WSAStartup failed, immediately exit - if (!table.wsa_reference_count) - { - return table; - } - - // we need a temporary socket for the IOCTL to load the functions - const wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (INVALID_SOCKET == localSocket.get()) - { - return table; - } - - const auto load_function_pointer = [](SOCKET lambdaSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { - constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(functionPtr)}; - DWORD unused_bytes{}; - const auto error{::WSAIoctl( - lambdaSocket, - controlCode, - &extensionGuid, - DWORD{sizeof(extensionGuid)}, - functionPtr, - bytes, - &unused_bytes, - nullptr, - nullptr)}; - return error == 0 ? S_OK : HRESULT_FROM_WIN32(::WSAGetLastError()); - }; - - // Load the functions into the table - if (FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_ACCEPTEX, &table.f.AcceptEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_CONNECTEX, &table.f.ConnectEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_DISCONNECTEX, &table.f.DisconnectEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_GETACCEPTEXSOCKADDRS, &table.f.GetAcceptExSockaddrs)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITFILE, &table.f.TransmitFile)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITPACKETS, &table.f.TransmitPackets)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSARECVMSG, &table.f.WSARecvMsg)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSASENDMSG, &table.f.WSASendMsg))) - { - // if any failed to be found, something is very broken - // all should load, or all should fail - ::memset(&table.f, 0, sizeof(table.f)); - } - - return table; - } - - // - // definitions for rio_extension_function_table - // - inline rio_extension_function_table rio_extension_function_table::load() WI_NOEXCEPT - { - rio_extension_function_table table{}; - // if WSAStartup failed, immediately exit - if (!table.wsa_reference_count) - { - return table; - } - - // we need a temporary socket for the IOCTL to load the functions - const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (INVALID_SOCKET == localSocket.get()) - { - return table; - } - - constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(table.f)}; - GUID rioGuid = WSAID_MULTIPLE_RIO; - - ::memset(&table.f, 0, bytes); - table.f.cbSize = bytes; - - DWORD unused_bytes{}; - if (::WSAIoctl( - localSocket.get(), - controlCode, - &rioGuid, - DWORD{sizeof(rioGuid)}, - &table.f, - bytes, - &unused_bytes, - nullptr, - nullptr) != 0) - { - LOG_IF_WIN32_ERROR(::WSAGetLastError()); - - ::memset(&table.f, 0, bytes); - } - return table; - } - } // namespace network } // namespace wil diff --git a/include/wil/resource.h b/include/wil/resource.h index 2a5bb79b..fb0d692d 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5081,7 +5081,7 @@ typedef shared_any shared_socket; typedef weak_any weak_socket; #endif // __WIL_WINSOCKAPI_STL -#if (defined _WS2TCPIP_H_) && !defined(__WIL_WS2TCPIP_H_)) || defined(WIL_DOXYGEN) +#if (defined(_WS2TCPIP_H_) && !defined(__WIL_WS2TCPIP_H_)) || defined(WIL_DOXYGEN) /// @cond #define __WIL_WS2TCPIP_H_ /// @endcond diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 6f579e16..2c974069 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -378,9 +378,9 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::network::socket_address::length == sizeof(SOCKADDR_INET)); wil::network::socket_address default_addr{}; + REQUIRE(default_addr.length() == sizeof(SOCKADDR_INET)); wil::network::socket_address test_v4_addr{&Test_in_addr}; wil::network::socket_address test_v4_addr2{&Test_in_addr2}; @@ -788,9 +788,9 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") { InitTestAddresses(); - REQUIRE(wil::network::socket_address::length == sizeof(SOCKADDR_INET)); wil::network::socket_address default_addr{}; + REQUIRE(default_addr.length() == sizeof(SOCKADDR_INET)); wil::network::socket_address test_v4_addr{&Test_in_addr}; wil::network::socket_address test_v4_addr2{&Test_in_addr2}; @@ -1219,7 +1219,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), test_address.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1297,7 +1297,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), test_address.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1376,7 +1376,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), test_address.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1454,7 +1454,7 @@ TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") test_address.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), wil::network::socket_address::length); + auto bind_error = ::bind(test_socket.get(), test_address.sockaddr(), test_address.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1834,7 +1834,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") listenAddress.set_port(TestPort); int gle = 0; - auto bind_error = ::bind(listeningSocket.get(), listenAddress.sockaddr(), wil::network::socket_address::length); + auto bind_error = ::bind(listeningSocket.get(), listenAddress.sockaddr(), listenAddress.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1852,7 +1852,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") REQUIRE(listen_error == 0); // the buffer to supply to AcceptEx to capture the address information - static constexpr size_t singleAddressOutputBufferSize = wil::network::socket_address::length + 16; + static constexpr size_t singleAddressOutputBufferSize = listenAddress.length() + 16; char acceptex_output_buffer[singleAddressOutputBufferSize * 2]{}; wil::unique_socket acceptSocket{::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)}; REQUIRE(acceptSocket.get() != INVALID_SOCKET); @@ -1906,7 +1906,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") connecting_from_address.set_port(0); // just an ephemeral port, ConnectEx will find a port gle = 0; - bind_error = ::bind(connectingSocket.get(), connecting_from_address.sockaddr(), wil::network::socket_address::length); + bind_error = ::bind(connectingSocket.get(), connecting_from_address.sockaddr(), connecting_from_address.length()); if (bind_error != 0) { gle = ::WSAGetLastError(); @@ -1916,7 +1916,7 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") gle = 0; auto connectex_return = test_table.f.ConnectEx( - connectingSocket.get(), listenAddress.sockaddr(), wil::network::socket_address::length, nullptr, 0, nullptr, &connectex_overlapped); + connectingSocket.get(), listenAddress.sockaddr(), listenAddress.length(), nullptr, 0, nullptr, &connectex_overlapped); if (!connectex_return) { gle = ::WSAGetLastError(); @@ -2053,9 +2053,9 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_local_addresses") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); - REQUIRE(test_addr.begin() != test_addr.end()); + REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); // verify operator-> const addr_info_iterator test_addr_iterator{test_addr.get()}; @@ -2070,7 +2070,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_local_addresses : %ws\n", address_string.c_str()); + wprintf(L"... resolve_local_addresses : %ws\n", address_string); ++count; } @@ -2080,7 +2080,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_localhost_addresses") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); @@ -2111,7 +2111,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_localhost_addresses : %ws\n", address_string.c_str()); + wprintf(L"... resolve_localhost_addresses : %ws\n", address_string); ++count; } @@ -2121,7 +2121,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_name") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); @@ -2134,7 +2134,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string.c_str()); + wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string); ++count; } @@ -2142,24 +2142,24 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") #endif } - SECTION("verify const addr_info iterators") + SECTION("verify const addr_info_iterator") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"localhost"); REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(test_addr_iterator->is_address_loopback()); - const auto& test_address_reference = *test_iterator; + const auto& test_address_reference = *test_addr_iterator; REQUIRE(test_address_reference.is_address_loopback()); #endif } - SECTION("verify addr_info iterator increment") + SECTION("verify addr_info_iterator increment") { -#ifdef WIL_ENABLE_EXCEPTIONS - const addr_info initial_addr = wil::network::resolve_name(L"localhost"); +#if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) + const wil::unique_addrinfo initial_addr = wil::network::resolve_name(L"localhost"); REQUIRE(addr_info_iterator::begin(initial_addr.get()) != addr_info_iterator::end()); auto total_count = 0; @@ -2177,9 +2177,9 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") #endif } - SECTION("verify addr_info iterator move behavior") + SECTION("verify addr_info_iterator move behavior") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); @@ -2201,9 +2201,9 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") #endif } - SECTION("verify addr_info iterator move assignment behavior") + SECTION("verify addr_info_iterator move assignment behavior") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); @@ -2211,14 +2211,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now - REQUIRE(moved_from_addr.begin() == moved_from_addr.end()); - REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); + REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) == addr_info_iterator::end()); + REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); // move to self moved_to_addr = std::move(moved_to_addr); - REQUIRE(moved_to_addr.begin() != moved_to_addr.end()); + REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); - for (const auto& address : moved_to_addr) + for (const auto& address : wil::network::make_range(moved_to_addr)) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2232,7 +2232,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info resolve_name failure") { -#ifdef WIL_ENABLE_EXCEPTIONS +#if (defined(WIL_ENABLE_EXCEPTIONS)) bool exception_thrown = false; try { From 40540a5dd1914ca6506e2ae0fafbbe17f7c7b62c Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sun, 19 Jan 2025 16:47:15 -0800 Subject: [PATCH 19/22] More simplification; more tidy work. --- include/wil/network.h | 475 ++++++++++++++++++-------------------- tests/NetworkingTests.cpp | 109 +++++---- 2 files changed, 285 insertions(+), 299 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index a1b92394..4ba12ae4 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -97,195 +97,6 @@ namespace network } #endif - struct WINSOCK_EXTENSION_FUNCTION_TABLE - { - LPFN_ACCEPTEX AcceptEx{nullptr}; - LPFN_CONNECTEX ConnectEx{nullptr}; - LPFN_DISCONNECTEX DisconnectEx{nullptr}; - LPFN_GETACCEPTEXSOCKADDRS GetAcceptExSockaddrs{nullptr}; - LPFN_TRANSMITFILE TransmitFile{nullptr}; - LPFN_TRANSMITPACKETS TransmitPackets{nullptr}; - LPFN_WSARECVMSG WSARecvMsg{nullptr}; - LPFN_WSASENDMSG WSASendMsg{nullptr}; - }; - - template - struct socket_extension_function_table_t; - using winsock_extension_function_table = socket_extension_function_table_t; - using rio_extension_function_table = socket_extension_function_table_t; - - template - struct socket_extension_function_table_t - { - static socket_extension_function_table_t load() WI_NOEXCEPT; - - ~socket_extension_function_table_t() WI_NOEXCEPT = default; - - // can copy, but the new object needs its own WSA reference count - // (getting a WSA reference count should be no-fail once the first reference it taken) - // - // IF the target could not get a WSA reference count - // OR - // IF we couldn't get our own WSA reference count - // (this should never happen the caller has a reference, but we failed to get a WSA reference) - // THEN - // this object cannot carry forward any function pointers - it must show successfully loaded == false - socket_extension_function_table_t(const socket_extension_function_table_t& rhs) WI_NOEXCEPT : - wsa_reference_count{WSAStartup_nothrow()} - { - if (!wsa_reference_count || !rhs.wsa_reference_count) - { - ::memset(&f, 0, sizeof(f)); - } - else - { - ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); - } - } - - socket_extension_function_table_t& operator=(const socket_extension_function_table_t& rhs) WI_NOEXCEPT - { - if (!wsa_reference_count || !rhs.wsa_reference_count) - { - ::memset(&f, 0, sizeof(f)); - } - else - { - ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); - } - - return *this; - } - - //! Returns true if all functions were loaded, holding a WSAStartup reference - WI_NODISCARD explicit operator bool() const WI_NOEXCEPT; - - F f{}; - - private: - // constructed via load() - socket_extension_function_table_t() WI_NOEXCEPT : - wsa_reference_count{WSAStartup_nothrow()} - { - } - - // must guarantee Winsock does not unload while we have dynamically loaded function pointers - const ::wil::network::unique_wsacleanup_call wsa_reference_count; - }; - - // - // explicit specializations for socket_extension_function_table_t - // - template <> - inline - socket_extension_function_table_t::operator bool() const WI_NOEXCEPT - { - return f.AcceptEx != nullptr; - } - - template <> - inline - socket_extension_function_table_t::operator bool() const WI_NOEXCEPT - { - return f.RIOReceive != nullptr; - } - - template <> - inline - winsock_extension_function_table socket_extension_function_table_t::load() WI_NOEXCEPT - { - winsock_extension_function_table table; - // if WSAStartup failed, immediately exit - if (!table.wsa_reference_count) - { - return table; - } - - // we need a temporary socket for the IOCTL to load the functions - const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (INVALID_SOCKET == localSocket.get()) - { - return table; - } - - const auto load_function_pointer = [](SOCKET lambdaSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { - constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(functionPtr)}; - DWORD unused_bytes{}; - const auto error{::WSAIoctl( - lambdaSocket, - controlCode, - &extensionGuid, - DWORD{sizeof(extensionGuid)}, - functionPtr, - bytes, - &unused_bytes, - nullptr, - nullptr)}; - return error == 0 ? S_OK : HRESULT_FROM_WIN32(::WSAGetLastError()); - }; - - if (FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_ACCEPTEX, &table.f.AcceptEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_CONNECTEX, &table.f.ConnectEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_DISCONNECTEX, &table.f.DisconnectEx)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_GETACCEPTEXSOCKADDRS, &table.f.GetAcceptExSockaddrs)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITFILE, &table.f.TransmitFile)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITPACKETS, &table.f.TransmitPackets)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSARECVMSG, &table.f.WSARecvMsg)) || - FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSASENDMSG, &table.f.WSASendMsg))) - { - // if any failed to be found, something is very broken - // all should load, or all should fail - ::memset(&table.f, 0, sizeof(table.f)); - } - - return table; - } - - template <> - inline - rio_extension_function_table socket_extension_function_table_t::load() WI_NOEXCEPT - { - rio_extension_function_table table{}; - // if WSAStartup failed, immediately exit - if (!table.wsa_reference_count) - { - return table; - } - - // we need a temporary socket for the IOCTL to load the functions - const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; - if (INVALID_SOCKET == localSocket.get()) - { - return table; - } - - constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; - constexpr DWORD bytes{sizeof(table.f)}; - GUID rioGuid = WSAID_MULTIPLE_RIO; - - ::memset(&table.f, 0, bytes); - table.f.cbSize = bytes; - - DWORD unused_bytes{}; - if (::WSAIoctl( - localSocket.get(), - controlCode, - &rioGuid, - DWORD{sizeof(rioGuid)}, - &table.f, - bytes, - &unused_bytes, - nullptr, - nullptr) != 0) - { - LOG_IF_WIN32_ERROR(::WSAGetLastError()); - - ::memset(&table.f, 0, bytes); - } - return table; - } - // // utility functions to compare inaddr types // @@ -296,7 +107,7 @@ namespace network [[nodiscard]] inline bool not_equals(const ::in_addr& lhs, const ::in_addr& rhs) WI_NOEXCEPT { - return !equals(lhs, rhs); + return !::wil::network::equals(lhs, rhs); } [[nodiscard]] inline bool equals(const ::in6_addr& lhs, const ::in6_addr& rhs) WI_NOEXCEPT @@ -306,7 +117,7 @@ namespace network [[nodiscard]] inline bool not_equals(const ::in6_addr& lhs, const ::in6_addr& rhs) WI_NOEXCEPT { - return !equals(lhs, rhs); + return !::wil::network::equals(lhs, rhs); } // @@ -328,13 +139,13 @@ namespace network //! Commonly used sockaddr* types that are using with TCPIP networking: //! //! sockaddr_storage / SOCKADDR_STORAGE - //! - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address + //! - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address (not just TCPIP related) //! sockaddr_in / SOCKADDR_IN //! - a sockaddr* derived type designed to contain an IPv4 address and port number //! sockaddr_in6 / SOCKADDR_IN6 //! - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info //! SOCKADDR_INET - //! - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP address + //! - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP IPv4 or IPV6 address //! in_addr / IN_ADDR //! - the raw address portion of a sockaddr_in //! in6_addr / IN6_ADDR @@ -353,7 +164,8 @@ namespace network class socket_address final { public: - explicit socket_address(ADDRESS_FAMILY family = AF_UNSPEC) WI_NOEXCEPT; + constexpr socket_address() WI_NOEXCEPT = default; + explicit socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT; template explicit socket_address(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT; explicit socket_address(const SOCKADDR_IN*) WI_NOEXCEPT; @@ -447,7 +259,9 @@ namespace network [[nodiscard]] const IN_ADDR* in_addr() const WI_NOEXCEPT; [[nodiscard]] const IN6_ADDR* in6_addr() const WI_NOEXCEPT; - constexpr int length() const + [[nodiscard]] SOCKADDR_STORAGE sockaddr_storage() const WI_NOEXCEPT; + + [[nodiscard]] constexpr int length() const { return sizeof(m_sockaddr); } @@ -474,38 +288,23 @@ namespace network return return_ipv6_address; } - // - // non-member swap - // + //! non-member swap inline void swap(::wil::network::socket_address& lhs, ::wil::network::socket_address& rhs) WI_NOEXCEPT { lhs.swap(rhs); } - //! class addr_info encapsulates the ADDRINFO structure - //! this structure contains a linked list of addresses returned from resolving a name via GetAddrInfo + //! class addr_info encapsulates the ADDRINFO-related structures returned from the socket functions + //! getaddrinfo, GetAddrInfoW, GetAddrInfoWEx //! iterator semantics are supported to safely access these addresses - template - class addr_info_iterator_t; - - using addr_info_ansi_iterator = addr_info_iterator_t; - using addr_info_iterator = addr_info_iterator_t; - // not defining a type for ADDRINFOEXA as that type is formally __declspec(deprecated) - using addr_infoex_iterator = addr_info_iterator_t; - - // template T supports all wil addrinfo types: ADDRINFOA, ADDRINFOW, ADDRINFOEXW, + // ! template T supports pointers to the 3 address structures: ADDRINFOA*, ADDRINFOW*, ADDRINFOEXW* template class addr_info_iterator_t { public: - static addr_info_iterator_t begin(const T* addrinfo_ptr) WI_NOEXCEPT - { - return addr_info_iterator_t{addrinfo_ptr}; - } - - static addr_info_iterator_t end() WI_NOEXCEPT + static constexpr addr_info_iterator_t end() WI_NOEXCEPT { - return addr_info_iterator_t{nullptr}; + return addr_info_iterator_t{}; } // defining iterator_traits allows STL functions to be used with this iterator class. @@ -521,9 +320,7 @@ namespace network using pointer = ::wil::network::socket_address*; using reference = ::wil::network::socket_address&; - explicit addr_info_iterator_t(nullptr_t) WI_NOEXCEPT - { - } + constexpr addr_info_iterator_t() WI_NOEXCEPT = default; explicit addr_info_iterator_t(const T* addrinfo) WI_NOEXCEPT : // must const cast so we can re-use this pointer as we walk the list @@ -613,49 +410,226 @@ namespace network ::wil::network::socket_address m_socket_address{}; }; // class addr_info_iterator_t - inline auto make_range(const ::wil::unique_addrinfo_ansi& addrinfo) - { - return ::wil::make_range(addr_info_ansi_iterator{addrinfo.get()}, addr_info_ansi_iterator{nullptr}); - } + using addr_info_ansi_iterator = addr_info_iterator_t; + using addr_info_iterator = addr_info_iterator_t; + // not defining a type for ADDRINFOEXA as that type is formally __declspec(deprecated) + using addr_infoex_iterator = addr_info_iterator_t; - inline auto make_range(const ::wil::unique_addrinfo& addrinfo) +#if defined(WIL_ENABLE_EXCEPTIONS) + //! wil function to capture resolving IP addresses assigned to the local machine, throwing on error + //! returning an RAII object containing the results + inline ::wil::unique_addrinfo resolve_local_addresses() WI_NOEXCEPT { - return ::wil::make_range(addr_info_iterator{addrinfo.get()}, addr_info_iterator{nullptr}); - } + constexpr auto* local_address_name_string = L""; + ADDRINFOW* addrResult{}; + if (::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) != 0) + { + THROW_WIN32(::WSAGetLastError()); + } - inline auto make_range(const ::wil::unique_addrinfoex& addrinfo) - { - return ::wil::make_range(addr_infoex_iterator{addrinfo.get()}, addr_infoex_iterator{nullptr}); + return ::wil::unique_addrinfo{addrResult}; } -#if defined(WIL_ENABLE_EXCEPTIONS) - //! wil function to capture resolving a name to a set of IP addresses, throwing on error + //! wil function to capture resolving the local-host (loopback) addresses, throwing on error //! returning an RAII object containing the results - inline ::wil::unique_addrinfo resolve_name(_In_ PCWSTR name) + inline ::wil::unique_addrinfo resolve_localhost_addresses() WI_NOEXCEPT { + constexpr auto* localhost_address_name_string = L"localhost"; ADDRINFOW* addrResult{}; - if (::GetAddrInfoW(name, nullptr, nullptr, &addrResult) != 0) + if (::GetAddrInfoW(localhost_address_name_string, nullptr, nullptr, &addrResult) != 0) { THROW_WIN32(::WSAGetLastError()); } return ::wil::unique_addrinfo{addrResult}; } +#endif - //! wil function to capture resolving the local machine to its set of IP addresses, throwing on error - //! returning an RAII object containing the results - inline ::wil::unique_addrinfo resolve_local_addresses() WI_NOEXCEPT + struct WINSOCK_EXTENSION_FUNCTION_TABLE + { + LPFN_ACCEPTEX AcceptEx{nullptr}; + LPFN_CONNECTEX ConnectEx{nullptr}; + LPFN_DISCONNECTEX DisconnectEx{nullptr}; + LPFN_GETACCEPTEXSOCKADDRS GetAcceptExSockaddrs{nullptr}; + LPFN_TRANSMITFILE TransmitFile{nullptr}; + LPFN_TRANSMITPACKETS TransmitPackets{nullptr}; + LPFN_WSARECVMSG WSARecvMsg{nullptr}; + LPFN_WSASENDMSG WSASendMsg{nullptr}; + }; + + template + struct socket_extension_function_table_t; + using winsock_extension_function_table = socket_extension_function_table_t; + using rio_extension_function_table = socket_extension_function_table_t; + + template + struct socket_extension_function_table_t + { + static socket_extension_function_table_t load() WI_NOEXCEPT; + + ~socket_extension_function_table_t() WI_NOEXCEPT = default; + + // can copy, but the new object needs its own WSA reference count + // (getting a WSA reference count should be no-fail once the first reference it taken) + // + // IF the target could not get a WSA reference count + // OR + // IF we couldn't get our own WSA reference count + // (this should never happen the caller has a reference, but we failed to get a WSA reference) + // THEN + // this object cannot carry forward any function pointers - it must show successfully loaded == false + socket_extension_function_table_t(const socket_extension_function_table_t& rhs) WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} + { + if (!wsa_reference_count || !rhs.wsa_reference_count) + { + ::memset(&f, 0, sizeof(f)); + } + else + { + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); + } + } + + socket_extension_function_table_t& operator=(const socket_extension_function_table_t& rhs) WI_NOEXCEPT + { + if (!wsa_reference_count || !rhs.wsa_reference_count) + { + ::memset(&f, 0, sizeof(f)); + } + else + { + ::memcpy_s(&f, sizeof(f), &rhs.f, sizeof(rhs.f)); + } + + return *this; + } + + //! Returns true if all functions were loaded, holding a WSAStartup reference + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT; + + F f{}; + + private: + // constructed via load() + socket_extension_function_table_t() WI_NOEXCEPT : + wsa_reference_count{WSAStartup_nothrow()} + { + } + + // must guarantee Winsock does not unload while we have dynamically loaded function pointers + const ::wil::network::unique_wsacleanup_call wsa_reference_count; + }; + + // + // explicit specializations for socket_extension_function_table_t + // + template <> + inline socket_extension_function_table_t::operator bool() const WI_NOEXCEPT { - return ::wil::network::resolve_name(L""); + return f.AcceptEx != nullptr; } - //! wil function to capture resolving the local-host addresses, throwing on error - //! returning an RAII object containing the results - inline ::wil::unique_addrinfo resolve_localhost_addresses() WI_NOEXCEPT + template <> + inline socket_extension_function_table_t::operator bool() const WI_NOEXCEPT { - return ::wil::network::resolve_name(L"localhost"); + return f.RIOReceive != nullptr; + } + + template <> + inline winsock_extension_function_table socket_extension_function_table_t< + WINSOCK_EXTENSION_FUNCTION_TABLE>::load() WI_NOEXCEPT + { + winsock_extension_function_table table; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) + { + return table; + } + + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + return table; + } + + const auto load_function_pointer = [](SOCKET lambdaSocket, GUID extensionGuid, void* functionPtr) WI_NOEXCEPT { + constexpr DWORD controlCode{SIO_GET_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(functionPtr)}; + DWORD unused_bytes{}; + const auto error{::WSAIoctl( + lambdaSocket, + controlCode, + &extensionGuid, + DWORD{sizeof(extensionGuid)}, + functionPtr, + bytes, + &unused_bytes, + nullptr, + nullptr)}; + return error == 0 ? S_OK : HRESULT_FROM_WIN32(::WSAGetLastError()); + }; + + if (FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_ACCEPTEX, &table.f.AcceptEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_CONNECTEX, &table.f.ConnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_DISCONNECTEX, &table.f.DisconnectEx)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_GETACCEPTEXSOCKADDRS, &table.f.GetAcceptExSockaddrs)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITFILE, &table.f.TransmitFile)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_TRANSMITPACKETS, &table.f.TransmitPackets)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSARECVMSG, &table.f.WSARecvMsg)) || + FAILED_LOG(load_function_pointer(localSocket.get(), WSAID_WSASENDMSG, &table.f.WSASendMsg))) + { + // if any failed to be found, something is very broken + // all should load, or all should fail + ::memset(&table.f, 0, sizeof(table.f)); + } + + return table; + } + + template <> + inline rio_extension_function_table socket_extension_function_table_t::load() WI_NOEXCEPT + { + rio_extension_function_table table{}; + // if WSAStartup failed, immediately exit + if (!table.wsa_reference_count) + { + return table; + } + + // we need a temporary socket for the IOCTL to load the functions + const ::wil::unique_socket localSocket{::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)}; + if (INVALID_SOCKET == localSocket.get()) + { + return table; + } + + constexpr DWORD controlCode{SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER}; + constexpr DWORD bytes{sizeof(table.f)}; + GUID rioGuid = WSAID_MULTIPLE_RIO; + + ::memset(&table.f, 0, bytes); + table.f.cbSize = bytes; + + DWORD unused_bytes{}; + if (::WSAIoctl( + localSocket.get(), + controlCode, + &rioGuid, + DWORD{sizeof(rioGuid)}, + &table.f, + bytes, + &unused_bytes, + nullptr, + nullptr) != 0) + { + LOG_IF_WIN32_ERROR(::WSAGetLastError()); + + ::memset(&table.f, 0, bytes); + } + return table; } -#endif // // socket_address definitions @@ -1192,12 +1166,7 @@ namespace network // addressLength == # of chars, not bytes DWORD addressLength{INET6_ADDRSTRLEN}; - if (::WSAAddressToStringW( - const_cast(sockaddr()), - length(), - nullptr, - address, - &addressLength) != 0) + if (::WSAAddressToStringW(const_cast(sockaddr()), length(), nullptr, address, &addressLength) != 0) { RETURN_WIN32(::WSAGetLastError()); } @@ -1218,8 +1187,7 @@ namespace network } DWORD addressLength{INET6_ADDRSTRLEN}; - if (::WSAAddressToStringA( - const_cast(sockaddr()), length(), nullptr, address, &addressLength) != 0) + if (::WSAAddressToStringA(const_cast(sockaddr()), length(), nullptr, address, &addressLength) != 0) { RETURN_WIN32(::WSAGetLastError()); } @@ -1347,6 +1315,13 @@ namespace network WI_ASSERT(family() == AF_INET6); return &m_sockaddr.Ipv6.sin6_addr; } + + inline SOCKADDR_STORAGE socket_address::sockaddr_storage() const noexcept + { + SOCKADDR_STORAGE return_sockaddr{}; + memcpy_s(&return_sockaddr, sizeof(return_sockaddr), &m_sockaddr, sizeof(m_sockaddr)); + return return_sockaddr; + } } // namespace network } // namespace wil diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 2c974069..f3db78b6 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -185,6 +185,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") REQUIRE(Test_any_in_addr_string == v4_addr.write_address()); #endif + SOCKADDR_STORAGE v4_addr_storage = v4_addr.sockaddr_storage(); + REQUIRE(v4_addr_storage.ss_family == AF_INET); + REQUIRE(0 == memcmp(&v4_addr_storage, v4_addr.sockaddr_in(), sizeof(SOCKADDR_IN))); + socket_address v6_addr{AF_INET6}; REQUIRE(v6_addr.family() == AF_INET6); REQUIRE(v6_addr.address_type() == NlatUnspecified); @@ -194,6 +198,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") #ifdef WIL_ENABLE_EXCEPTIONS REQUIRE(Test_any_in6_addr_string == v6_addr.write_address()); #endif + + SOCKADDR_STORAGE v6_addr_storage = v6_addr.sockaddr_storage(); + REQUIRE(v6_addr_storage.ss_family == AF_INET6); + REQUIRE(0 == memcmp(&v6_addr_storage, v6_addr.sockaddr_in6(), sizeof(SOCKADDR_IN6))); } SECTION("socket_address(const SOCKADDR*, T)") @@ -213,6 +221,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); #endif + SOCKADDR_STORAGE v4_addr_storage = v4_addr.sockaddr_storage(); + REQUIRE(v4_addr_storage.ss_family == AF_INET); + REQUIRE(0 == memcmp(&v4_addr_storage, v4_addr.sockaddr_in(), sizeof(SOCKADDR_IN))); + SOCKADDR_IN6 v6_test_sockaddr{}; v6_test_sockaddr.sin6_family = AF_INET6; // these raw value are in network byte order @@ -232,6 +244,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") #ifdef WIL_ENABLE_EXCEPTIONS REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); #endif + + SOCKADDR_STORAGE v6_addr_storage = v6_addr.sockaddr_storage(); + REQUIRE(v6_addr_storage.ss_family == AF_INET6); + REQUIRE(0 == memcmp(&v6_addr_storage, v6_addr.sockaddr_in6(), sizeof(SOCKADDR_IN6))); } SECTION("socket_address(const SOCKADDR_IN*)") @@ -250,6 +266,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") #ifdef WIL_ENABLE_EXCEPTIONS REQUIRE(Test_linklocal_in_addr_string == v4_addr.write_address()); #endif + + SOCKADDR_STORAGE v4_addr_storage = v4_addr.sockaddr_storage(); + REQUIRE(v4_addr_storage.ss_family == AF_INET); + REQUIRE(0 == memcmp(&v4_addr_storage, v4_addr.sockaddr_in(), sizeof(SOCKADDR_IN))); } SECTION("socket_address(const SOCKADDR_IN6*)") @@ -273,6 +293,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") #ifdef WIL_ENABLE_EXCEPTIONS REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); #endif + + SOCKADDR_STORAGE v6_addr_storage = v6_addr.sockaddr_storage(); + REQUIRE(v6_addr_storage.ss_family == AF_INET6); + REQUIRE(0 == memcmp(&v6_addr_storage, v6_addr.sockaddr_in6(), sizeof(SOCKADDR_IN6))); } SECTION("socket_address(const SOCKADDR_INET*)") @@ -296,6 +320,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") #endif REQUIRE(0 == memcmp(&v4_inet_addr, v4_addr.sockaddr_inet(), sizeof(SOCKADDR_INET))); + SOCKADDR_STORAGE v4_addr_storage = v4_addr.sockaddr_storage(); + REQUIRE(v4_addr_storage.ss_family == AF_INET); + REQUIRE(0 == memcmp(&v4_addr_storage, v4_addr.sockaddr_in(), sizeof(SOCKADDR_IN))); + SOCKADDR_IN6 v6_test_sockaddr{}; v6_test_sockaddr.sin6_family = AF_INET6; // these raw value are in network byte order @@ -319,6 +347,10 @@ TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") REQUIRE(Test_linklocal_in6_addr_string == v6_addr.write_address()); #endif REQUIRE(0 == memcmp(&v6_inet_addr, v6_addr.sockaddr_inet(), sizeof(SOCKADDR_INET))); + + SOCKADDR_STORAGE v6_addr_storage = v6_addr.sockaddr_storage(); + REQUIRE(v6_addr_storage.ss_family == AF_INET6); + REQUIRE(0 == memcmp(&v6_addr_storage, v6_addr.sockaddr_in6(), sizeof(SOCKADDR_IN6))); } SECTION("socket_address(const SOCKET_ADDRESS*)") @@ -2055,14 +2087,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { #if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); - REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); // verify operator-> const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(!test_addr_iterator->is_address_loopback()); uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) + for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2082,14 +2114,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { #if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); // verify operator-> const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(test_addr_iterator->is_address_loopback()); uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) + for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2122,11 +2154,11 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_name") { #if (defined(WIL_ENABLE_EXCEPTIONS)) - const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); + REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator::begin(test_addr.get()), addr_info_iterator::end())) + for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2134,7 +2166,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_name(..localmachine) : %ws\n", address_string); + wprintf(L"... resolve_local_addresses : %ws\n", address_string); ++count; } @@ -2145,8 +2177,8 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify const addr_info_iterator") { #if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) - const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); + REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(test_addr_iterator->is_address_loopback()); @@ -2159,19 +2191,19 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info_iterator increment") { #if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) - const wil::unique_addrinfo initial_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(addr_info_iterator::begin(initial_addr.get()) != addr_info_iterator::end()); + const wil::unique_addrinfo initial_addr = wil::network::resolve_localhost_addresses(); + REQUIRE(addr_info_iterator{initial_addr.get()} != addr_info_iterator::end()); auto total_count = 0; - for (auto it = addr_info_iterator::begin(initial_addr.get()); it != addr_info_iterator::end(); ++it) + for (auto it = addr_info_iterator{initial_addr.get()}; it != addr_info_iterator::end(); ++it) { ++total_count; } - const wil::unique_addrinfo test_addr = wil::network::resolve_name(L"localhost"); - REQUIRE(addr_info_iterator::begin(test_addr.get()) != addr_info_iterator::end()); + const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); + REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); - addr_info_iterator test_iterator = addr_info_iterator::begin(test_addr.get()); + addr_info_iterator test_iterator = addr_info_iterator{test_addr.get()}; test_iterator += total_count; REQUIRE(test_iterator == addr_info_iterator::end()); #endif @@ -2180,15 +2212,15 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info_iterator move behavior") { #if (defined(WIL_ENABLE_EXCEPTIONS)) - wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); + wil::unique_addrinfo moved_from_addr = wil::network::resolve_local_addresses(); + REQUIRE(addr_info_iterator{moved_from_addr.get()} != addr_info_iterator::end()); const wil::unique_addrinfo moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now - REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) == addr_info_iterator::end()); - REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_from_addr.get()} == addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); - for (const auto& address : wil::make_range(addr_info_iterator::begin(moved_to_addr.get()), addr_info_iterator::end())) + for (const auto& address : wil::make_range(addr_info_iterator{moved_to_addr.get()}, addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2196,7 +2228,7 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... moved resolve_name(..localmachine) : %ws\n", address_string); + wprintf(L"... moved resolve_local_addresses : %ws\n", address_string); } #endif } @@ -2204,21 +2236,21 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify addr_info_iterator move assignment behavior") { #if (defined(WIL_ENABLE_EXCEPTIONS)) - wil::unique_addrinfo moved_from_addr = wil::network::resolve_name(L"..localmachine"); - REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) != addr_info_iterator::end()); + wil::unique_addrinfo moved_from_addr = wil::network::resolve_local_addresses(); + REQUIRE(addr_info_iterator{moved_from_addr.get()} != addr_info_iterator::end()); wil::unique_addrinfo moved_to_addr{wil::network::resolve_local_addresses()}; moved_to_addr = std::move(moved_from_addr); // moved_from_addr should be end() now - REQUIRE(addr_info_iterator::begin(moved_from_addr.get()) == addr_info_iterator::end()); - REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_from_addr.get()} == addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); // move to self moved_to_addr = std::move(moved_to_addr); - REQUIRE(addr_info_iterator::begin(moved_to_addr.get()) != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); - for (const auto& address : wil::network::make_range(moved_to_addr)) + for (const auto& address : wil::make_range(addr_info_iterator{moved_to_addr.get()}, addr_info_iterator::end())) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2227,27 +2259,6 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); } -#endif - } - - SECTION("verify addr_info resolve_name failure") - { -#if (defined(WIL_ENABLE_EXCEPTIONS)) - bool exception_thrown = false; - try - { - const auto test_addr = wil::network::resolve_name(L"...xyz.xyz..."); - } - catch (const wil::ResultException& e) - { - exception_thrown = true; - REQUIRE(e.GetErrorCode() == HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND)); - } - catch (...) - { - REQUIRE(false); - } - REQUIRE(exception_thrown); #endif } } \ No newline at end of file From be244d5de405bfcf334a37e971cbefb891c26bb1 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sun, 19 Jan 2025 17:10:46 -0800 Subject: [PATCH 20/22] Add tests for other addr_info types --- tests/NetworkingTests.cpp | 176 +++++++++++++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 23 deletions(-) diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index f3db78b6..9c37df0f 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -2077,7 +2077,9 @@ TEST_CASE("NetworkingTests::Verifying_function_tables", "[networking]") TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { + using wil::network::addr_info_ansi_iterator; using wil::network::addr_info_iterator; + using wil::network::addr_infoex_iterator; using wil::network::equals; const auto cleanup = wil::network::WSAStartup_nothrow(); @@ -2151,29 +2153,6 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") #endif } - SECTION("verify resolve_name") - { -#if (defined(WIL_ENABLE_EXCEPTIONS)) - const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); - REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); - - uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) - { - const auto family = address.family(); - REQUIRE((family == AF_INET || family == AF_INET6)); - REQUIRE(!address.is_address_loopback()); - - wil::network::socket_address_wstring address_string; - REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_local_addresses : %ws\n", address_string); - ++count; - } - - REQUIRE(count > 0); -#endif - } - SECTION("verify const addr_info_iterator") { #if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) @@ -2258,7 +2237,158 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") wil::network::socket_address_wstring address_string; REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... move assignment resolve_local_addresses : %ws\n", address_string); } #endif } + + // retest with unique_addrinfo_ansi + SECTION("verify addr_info_ansi_iterator increment") + { + wil::unique_addrinfo_ansi initial_addr; + REQUIRE(0 == getaddrinfo("localhost", nullptr, nullptr, initial_addr.addressof())); + REQUIRE(addr_info_ansi_iterator{initial_addr.get()} != addr_info_ansi_iterator::end()); + + auto total_count = 0; + for (auto it = addr_info_ansi_iterator{initial_addr.get()}; it != addr_info_ansi_iterator::end(); ++it) + { + ++total_count; + } + + wil::unique_addrinfo_ansi test_addr; + REQUIRE(0 == getaddrinfo("localhost", nullptr, nullptr, test_addr.addressof())); + REQUIRE(addr_info_ansi_iterator{test_addr.get()} != addr_info_ansi_iterator::end()); + + addr_info_ansi_iterator test_iterator = addr_info_ansi_iterator{test_addr.get()}; + test_iterator += total_count; + REQUIRE(test_iterator == addr_info_ansi_iterator::end()); + } + + SECTION("verify addr_info_ansi_iterator move behavior") + { + wil::unique_addrinfo_ansi moved_from_addr; + REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_from_addr.addressof())); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != addr_info_ansi_iterator::end()); + + const wil::unique_addrinfo_ansi moved_to_addr = std::move(moved_from_addr); + // moved_from_addr should be end() now + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + + for (const auto& address : wil::make_range(addr_info_ansi_iterator{moved_to_addr.get()}, addr_info_ansi_iterator::end())) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... moved getaddrinfo(unique_addrinfo_ansi) : %ws\n", address_string); + } + } + + SECTION("verify addr_info_ansi_iterator move assignment behavior") + { + wil::unique_addrinfo_ansi moved_from_addr; + REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_from_addr.addressof())); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != addr_info_ansi_iterator::end()); + + wil::unique_addrinfo_ansi moved_to_addr; + REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_to_addr.addressof())); + moved_to_addr = std::move(moved_from_addr); + + // moved_from_addr should be end() now + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + + // move to self + moved_to_addr = std::move(moved_to_addr); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + + for (const auto& address : wil::make_range(addr_info_ansi_iterator{moved_to_addr.get()}, addr_info_ansi_iterator::end())) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... move assignment getaddrinfo(unique_addrinfo_ansi) : %ws\n", address_string); + } + } + + // retest with unique_addrinfoex + SECTION("verify addr_info_ansi_iterator increment") + { + wil::unique_addrinfoex initial_addr; + REQUIRE(0 == GetAddrInfoExW(L"localhost", nullptr, NS_ALL, nullptr, nullptr, initial_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); + REQUIRE(addr_infoex_iterator{initial_addr.get()} != addr_infoex_iterator::end()); + + auto total_count = 0; + for (auto it = addr_infoex_iterator{initial_addr.get()}; it != addr_infoex_iterator::end(); ++it) + { + ++total_count; + } + + wil::unique_addrinfoex test_addr; + REQUIRE(0 == GetAddrInfoExW(L"localhost", nullptr, NS_ALL, nullptr, nullptr, test_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); + REQUIRE(addr_infoex_iterator{test_addr.get()} != addr_infoex_iterator::end()); + + addr_infoex_iterator test_iterator = addr_infoex_iterator{test_addr.get()}; + test_iterator += total_count; + REQUIRE(test_iterator == addr_infoex_iterator::end()); + } + + SECTION("verify addr_infoex_iterator move behavior") + { + wil::unique_addrinfoex moved_from_addr; + REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_from_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != addr_infoex_iterator::end()); + + const wil::unique_addrinfoex moved_to_addr = std::move(moved_from_addr); + // moved_from_addr should be end() now + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + + for (const auto& address : wil::make_range(addr_infoex_iterator{moved_to_addr.get()}, addr_infoex_iterator::end())) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... moved GetAddrInfoExW(unique_addrinfoex) : %ws\n", address_string); + } + } + + SECTION("verify addr_infoex_iterator move assignment behavior") + { + wil::unique_addrinfoex moved_from_addr; + REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_from_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != addr_infoex_iterator::end()); + + wil::unique_addrinfoex moved_to_addr; + REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_to_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); + moved_to_addr = std::move(moved_from_addr); + + // moved_from_addr should be end() now + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + + // move to self + moved_to_addr = std::move(moved_to_addr); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + + for (const auto& address : wil::make_range(addr_infoex_iterator{moved_to_addr.get()}, addr_infoex_iterator::end())) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... move assignment GetAddrInfoExW(unique_addrinfoex) : %ws\n", address_string); + } + } } \ No newline at end of file From 66d3582bf2c3f626a4b07cb9928d66989741f89e Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 25 Jan 2025 17:25:34 -0800 Subject: [PATCH 21/22] Fixing comparison operator cases. Fixing comments. --- include/wil/network.h | 158 ++++++++++++++++++++------------------ tests/NetworkingTests.cpp | 51 +++++++++++- 2 files changed, 129 insertions(+), 80 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index 4ba12ae4..c0923276 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -8,9 +8,9 @@ // PARTICULAR PURPOSE AND NONINFRINGEMENT. // //********************************************************* -//! @file -//! Helpers for using BSD sockets and Winsock functions and structures. -//! Does not require the use of the STL or C++ exceptions (see _nothrow functions) +// @file +// Helpers for using BSD sockets and Winsock functions and structures. +// Does not require the use of the STL or C++ exceptions (see _nothrow functions) #ifndef __WIL_NETWORK_INCLUDED #define __WIL_NETWORK_INCLUDED @@ -18,25 +18,25 @@ #error This header is not supported in kernel-mode. #endif -//! define WIN32_LEAN_AND_MEAN at the project level to avoid windows.h including older winsock 1.1 headers from winsock.h -//! as including both the Winsock 1.1 header 'winsock.h' and the Winsock 2 header 'winsock2.h' will create compiler errors -//! alternatively, including winsock2.h before windows.h to prevent inclusion of the Winsock 1.1 winsock.h header from within windows.h -//! note: winsock2.h will include windows.h if not already included -//! -//! define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* functions in ws2tcpip.h -//! -//! define INCL_WINSOCK_API_TYPEDEFS at the project level to make function typedefs available across various networking headers -//! note, winsock2.h defaults is to not include function typedefs - but these can be necessary when supporting multiple OS versions -//! -//! Link libs for functions referenced in this file: ws2_32.lib, ntdll.lib, and Fwpuclnt.lib (for secure socket functions) +// define WIN32_LEAN_AND_MEAN at the project level to avoid windows.h including older winsock 1.1 headers from winsock.h +// as including both the Winsock 1.1 header 'winsock.h' and the Winsock 2 header 'winsock2.h' will create compiler errors +// alternatively, including winsock2.h before windows.h to prevent inclusion of the Winsock 1.1 winsock.h header from within windows.h +// note: winsock2.h will include windows.h if not already included +// +// define _SECURE_SOCKET_TYPES_DEFINED_ at the project level to have access to SocketSecurity* functions in ws2tcpip.h +// +// define INCL_WINSOCK_API_TYPEDEFS at the project level to make function typedefs available across various networking headers +// note, winsock2.h defaults is to not include function typedefs - but these can be necessary when supporting multiple OS versions +// +// Link libs for functions referenced in this file: ws2_32.lib, ntdll.lib, and Fwpuclnt.lib (for secure socket functions) #if !defined(_WINSOCK2API_) && defined(_WINSOCKAPI_) #error The Winsock 1.1 winsock.h header was included before the Winsock 2 winsock2.h header - this will cause compilation errors - define WIN32_LEAN_AND_MEAN to avoid winsock.h included by windows.h, or include winsock2.h before windows.h #endif -//! Including Winsock and networking headers in the below specific sequence -//! These headers have many intra-header dependencies, creating difficulties when needing access to various functions and types -//! This specific sequence should compile correctly to give access to all available functions and types +// Including Winsock and networking headers in the below specific sequence +// These headers have many intra-header dependencies, creating difficulties when needing access to various functions and types +// This specific sequence should compile correctly to give access to all available functions and types #include #include #include @@ -52,16 +52,16 @@ namespace wil { -//! Functions and classes that support networking operations and structures +// Functions and classes that support networking operations and structures namespace network { - //! A type that calls WSACleanup on destruction (or reset()). - //! WSAStartup must be called for the lifetime of all Winsock APIs (synchronous and asynchronous) - //! WSACleanup will unload the full Winsock catalog - all the libraries - with the final reference - //! which can lead to crashes if socket APIs are still being used after the final WSACleanup is called + // A type that calls WSACleanup on destruction (or reset()). + // WSAStartup must be called for the lifetime of all Winsock APIs (synchronous and asynchronous) + // WSACleanup will unload the full Winsock catalog - all the libraries - with the final reference + // which can lead to crashes if socket APIs are still being used after the final WSACleanup is called using unique_wsacleanup_call = ::wil::unique_call; - //! Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed + // Calls WSAStartup; returns an RAII object that reverts, the RAII object will resolve to bool 'false' if failed WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT { WSADATA unused_data{}; @@ -79,7 +79,7 @@ namespace network return return_cleanup; } - //! Calls WSAStartup and fail-fasts on error; returns an RAII object that reverts + // Calls WSAStartup and fail-fasts on error; returns an RAII object that reverts WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup_failfast() WI_NOEXCEPT { WSADATA unused_data{}; @@ -88,7 +88,7 @@ namespace network } #if defined(WIL_ENABLE_EXCEPTIONS) - //! Calls WSAStartup and throws on error; returns an RAII object that reverts + // Calls WSAStartup and throws on error; returns an RAII object that reverts WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup() { WSADATA unused_data{}; @@ -121,40 +121,40 @@ namespace network } // - //! encapsulates working with the sockaddr datatype - //! - //! sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) - //! 'derived' structures are cast back to sockaddr* (so the initial struct members must be aligned) - //! - //! this data type was built to be 'extensible' so new network types could create their own address structures - //! - appending fields to the initial fields of the sockaddr - //! - //! note that the address and port fields of TCPIP sockaddr* types were designed to be encoded in network-byte order - //! - hence the common use of "host-to-network" and "network-to-host" APIs, e.g. htons(), htonl(), ntohs(), ntohl() - //! - //! Socket APIs that accept a socket address will accept 2 fields: - //! - the sockaddr* (the address of the derived sockaddr type, cast down to a sockaddr*) - //! - the length of the 'derived' socket address structure referenced by the sockaddr* - //! - //! Commonly used sockaddr* types that are using with TCPIP networking: - //! - //! sockaddr_storage / SOCKADDR_STORAGE - //! - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address (not just TCPIP related) - //! sockaddr_in / SOCKADDR_IN - //! - a sockaddr* derived type designed to contain an IPv4 address and port number - //! sockaddr_in6 / SOCKADDR_IN6 - //! - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info - //! SOCKADDR_INET - //! - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP IPv4 or IPV6 address - //! in_addr / IN_ADDR - //! - the raw address portion of a sockaddr_in - //! in6_addr / IN6_ADDR - //! - the raw address portion of a sockaddr_in6 - //! - //! SOCKET_ADDRESS - //! - not a derived sockaddr* type - //! - a structure containing both a sockaddr* and its length fields, returned from some networking functions - //! + // encapsulates working with the sockaddr datatype + // + // sockaddr is a generic type - similar to a base class, but designed for C with BSD sockets (1983-ish) + // 'derived' structures are cast back to sockaddr* (so the initial struct members must be aligned) + // + // this data type was built to be 'extensible' so new network types could create their own address structures + // - appending fields to the initial fields of the sockaddr + // + // note that the address and port fields of TCPIP sockaddr* types were designed to be encoded in network-byte order + // - hence the common use of "host-to-network" and "network-to-host" APIs, e.g. htons(), htonl(), ntohs(), ntohl() + // + // Socket APIs that accept a socket address will accept 2 fields: + // - the sockaddr* (the address of the derived sockaddr type, cast down to a sockaddr*) + // - the length of the 'derived' socket address structure referenced by the sockaddr* + // + // Commonly used sockaddr* types that are using with TCPIP networking: + // + // sockaddr_storage / SOCKADDR_STORAGE + // - a sockaddr* derived type that is guaranteed to be large enough to hold any possible socket address (not just TCPIP related) + // sockaddr_in / SOCKADDR_IN + // - a sockaddr* derived type designed to contain an IPv4 address and port number + // sockaddr_in6 / SOCKADDR_IN6 + // - a sockaddr* derived type designed to contain an IPv6 address, port, scope id, and flow info + // SOCKADDR_INET + // - a union of sockaddr_in and sockaddr_in6 -- i.e., large enough to contain any TCPIP IPv4 or IPV6 address + // in_addr / IN_ADDR + // - the raw address portion of a sockaddr_in + // in6_addr / IN6_ADDR + // - the raw address portion of a sockaddr_in6 + // + // SOCKET_ADDRESS + // - not a derived sockaddr* type + // - a structure containing both a sockaddr* and its length fields, returned from some networking functions + // // declaring char-arrays large enough for any IPv4 or IPv6 address + optional fields static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN); @@ -230,7 +230,7 @@ namespace network HRESULT write_complete_address_nothrow(socket_address_wstring& address) const WI_NOEXCEPT; HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; -#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) +#if (WIL_USE_STL && defined(WIL_ENABLE_EXCEPTIONS)) || defined(WIL_DOXYGEN) [[nodiscard]] ::std::wstring write_address() const; [[nodiscard]] ::std::wstring write_complete_address() const; #endif @@ -288,15 +288,15 @@ namespace network return return_ipv6_address; } - //! non-member swap + // non-member swap inline void swap(::wil::network::socket_address& lhs, ::wil::network::socket_address& rhs) WI_NOEXCEPT { lhs.swap(rhs); } - //! class addr_info encapsulates the ADDRINFO-related structures returned from the socket functions - //! getaddrinfo, GetAddrInfoW, GetAddrInfoWEx - //! iterator semantics are supported to safely access these addresses + // class addr_info encapsulates the ADDRINFO-related structures returned from the socket functions + // getaddrinfo, GetAddrInfoW, GetAddrInfoWEx + // iterator semantics are supported to safely access these addresses // ! template T supports pointers to the 3 address structures: ADDRINFOA*, ADDRINFOW*, ADDRINFOEXW* template class addr_info_iterator_t @@ -311,7 +311,7 @@ namespace network // Notice this is a forward_iterator // - does not support random-access (e.g. vector::iterator) // - does not support bidirectional access (e.g. list::iterator) -#if defined(_ITERATOR_) || defined(WIL_DOXYGEN) +#if WIL_USE_STL || defined(WIL_DOXYGEN) using iterator_category = ::std::forward_iterator_tag; #endif using value_type = ::wil::network::socket_address; @@ -416,9 +416,9 @@ namespace network using addr_infoex_iterator = addr_info_iterator_t; #if defined(WIL_ENABLE_EXCEPTIONS) - //! wil function to capture resolving IP addresses assigned to the local machine, throwing on error - //! returning an RAII object containing the results - inline ::wil::unique_addrinfo resolve_local_addresses() WI_NOEXCEPT + // wil function to capture resolving IP addresses assigned to the local machine, throwing on error + // returning an RAII object containing the results + inline ::wil::unique_addrinfo resolve_local_addresses() { constexpr auto* local_address_name_string = L""; ADDRINFOW* addrResult{}; @@ -430,9 +430,9 @@ namespace network return ::wil::unique_addrinfo{addrResult}; } - //! wil function to capture resolving the local-host (loopback) addresses, throwing on error - //! returning an RAII object containing the results - inline ::wil::unique_addrinfo resolve_localhost_addresses() WI_NOEXCEPT + // wil function to capture resolving the local-host (loopback) addresses, throwing on error + // returning an RAII object containing the results + inline ::wil::unique_addrinfo resolve_localhost_addresses() { constexpr auto* localhost_address_name_string = L"localhost"; ADDRINFOW* addrResult{}; @@ -505,7 +505,7 @@ namespace network return *this; } - //! Returns true if all functions were loaded, holding a WSAStartup reference + // Returns true if all functions were loaded, holding a WSAStartup reference WI_NODISCARD explicit operator bool() const WI_NOEXCEPT; F f{}; @@ -742,7 +742,8 @@ namespace network return lhs_port < rhs_port; } - return true; + // must be exactly equal, so not less-than + return false; } case AF_INET6: @@ -779,7 +780,8 @@ namespace network return lhs_flow_info < rhs_flow_info; } - return true; + // must be exactly equal, so not less-than + return false; } default: @@ -791,6 +793,10 @@ namespace network inline bool socket_address::operator>(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT { + if (*this == rhs) + { + return false; + } return !(*this < rhs); } @@ -1083,7 +1089,7 @@ namespace network return E_INVALIDARG; } -#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) +#if (WIL_USE_STL && defined(WIL_ENABLE_EXCEPTIONS)) || defined(WIL_DOXYGEN) inline ::std::wstring socket_address::write_address() const { ::wil::network::socket_address_wstring returnString{}; @@ -1143,7 +1149,7 @@ namespace network return S_OK; } -#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(_STRING_) || defined(WIL_DOXYGEN)) +#if (WIL_USE_STL && defined(WIL_ENABLE_EXCEPTIONS)) || defined(WIL_DOXYGEN) inline ::std::wstring socket_address::write_complete_address() const { ::wil::network::socket_address_wstring returnString{}; diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 9c37df0f..498746f1 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -452,11 +452,15 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_addr.flow_info() == 0); REQUIRE(test_v4_addr == test_v4_addr); + REQUIRE(!(test_v4_addr != test_v4_addr)); + REQUIRE(!(test_v4_addr < test_v4_addr)); + REQUIRE(!(test_v4_addr > test_v4_addr)); REQUIRE(test_v4_addr != test_v4_addr2); REQUIRE(test_v4_addr < test_v4_addr2); REQUIRE(test_v4_addr2 > test_v4_addr); REQUIRE(test_v4_addr != default_addr); REQUIRE(test_v4_addr > default_addr); + REQUIRE(default_addr < test_v4_addr); } SECTION("IPv4 in_addr with port properties") @@ -474,11 +478,15 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_addr_with_port.flow_info() == 0); REQUIRE(test_v4_addr_with_port == test_v4_addr_with_port); + REQUIRE(!(test_v4_addr_with_port != test_v4_addr_with_port)); + REQUIRE(!(test_v4_addr_with_port < test_v4_addr_with_port)); + REQUIRE(!(test_v4_addr_with_port > test_v4_addr_with_port)); REQUIRE(test_v4_addr_with_port != default_addr); REQUIRE(test_v4_addr_with_port != test_v4_addr); REQUIRE(test_v4_addr_with_port > test_v4_addr); REQUIRE(test_v4_addr_with_port < test_v4_addr2); REQUIRE(test_v4_addr_with_port > default_addr); + REQUIRE(default_addr < test_v4_addr_with_port); } SECTION("IPv6 in6_addr properties") @@ -496,13 +504,18 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_addr.flow_info() == 0); REQUIRE(test_v6_addr == test_v6_addr); + REQUIRE(!(test_v6_addr != test_v6_addr)); + REQUIRE(!(test_v6_addr < test_v6_addr)); + REQUIRE(!(test_v6_addr > test_v6_addr)); REQUIRE(test_v6_addr != test_v6_addr2); REQUIRE(test_v6_addr < test_v6_addr2); REQUIRE(test_v6_addr2 > test_v6_addr); REQUIRE(test_v6_addr != test_v4_addr); REQUIRE(test_v6_addr > test_v4_addr); + REQUIRE(test_v4_addr < test_v6_addr); REQUIRE(test_v6_addr != default_addr); REQUIRE(test_v6_addr > default_addr); + REQUIRE(default_addr < test_v6_addr); } SECTION("IPv6 in6_addr with port properties") @@ -520,7 +533,12 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_addr_with_port.flow_info() == 0); REQUIRE(test_v6_addr_with_port == test_v6_addr_with_port); + REQUIRE(!(test_v6_addr_with_port != test_v6_addr_with_port)); + REQUIRE(!(test_v6_addr_with_port < test_v6_addr_with_port)); + REQUIRE(!(test_v6_addr_with_port > test_v6_addr_with_port)); REQUIRE(test_v6_addr_with_port != test_v4_addr); + REQUIRE(test_v6_addr_with_port > test_v4_addr); + REQUIRE(test_v4_addr < test_v6_addr_with_port); REQUIRE(test_v6_addr_with_port != test_v4_addr_with_port); REQUIRE(test_v6_addr_with_port != test_v6_addr); REQUIRE(test_v6_addr_with_port != test_v6_addr2); @@ -528,6 +546,7 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_addr_with_port < test_v6_addr2); REQUIRE(test_v6_addr_with_port != default_addr); REQUIRE(test_v6_addr_with_port > default_addr); + REQUIRE(default_addr < test_v6_addr_with_port); } SECTION("IPv4 link-local in_addr properties") @@ -543,6 +562,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_linklocal_addr.flow_info() == 0); REQUIRE(test_v4_linklocal_addr == test_v4_linklocal_addr); + REQUIRE(!(test_v4_linklocal_addr != test_v4_linklocal_addr)); + REQUIRE(!(test_v4_linklocal_addr < test_v4_linklocal_addr)); + REQUIRE(!(test_v4_linklocal_addr > test_v4_linklocal_addr)); REQUIRE(test_v4_linklocal_addr != default_addr); REQUIRE(test_v4_linklocal_addr != test_v4_addr); REQUIRE(test_v4_linklocal_addr != test_v4_addr_with_port); @@ -564,6 +586,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_linklocal_addr_with_port.flow_info() == 0); REQUIRE(test_v4_linklocal_addr_with_port == test_v4_linklocal_addr_with_port); + REQUIRE(!(test_v4_linklocal_addr_with_port != test_v4_linklocal_addr_with_port)); + REQUIRE(!(test_v4_linklocal_addr_with_port < test_v4_linklocal_addr_with_port)); + REQUIRE(!(test_v4_linklocal_addr_with_port > test_v4_linklocal_addr_with_port)); REQUIRE(test_v4_linklocal_addr_with_port != default_addr); REQUIRE(test_v4_linklocal_addr_with_port != test_v4_addr); REQUIRE(test_v4_linklocal_addr_with_port != test_v4_addr_with_port); @@ -585,6 +610,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_linklocal_addr.flow_info() == 0); REQUIRE(test_v6_linklocal_addr == test_v6_linklocal_addr); + REQUIRE(!(test_v6_linklocal_addr != test_v6_linklocal_addr)); + REQUIRE(!(test_v6_linklocal_addr < test_v6_linklocal_addr)); + REQUIRE(!(test_v6_linklocal_addr > test_v6_linklocal_addr)); REQUIRE(test_v6_linklocal_addr != default_addr); REQUIRE(test_v6_linklocal_addr != test_v4_addr); REQUIRE(test_v6_linklocal_addr != test_v4_addr_with_port); @@ -608,6 +636,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_linklocal_addr_with_port.flow_info() == 0); REQUIRE(test_v6_linklocal_addr_with_port == test_v6_linklocal_addr_with_port); + REQUIRE(!(test_v6_linklocal_addr_with_port != test_v6_linklocal_addr_with_port)); + REQUIRE(!(test_v6_linklocal_addr_with_port < test_v6_linklocal_addr_with_port)); + REQUIRE(!(test_v6_linklocal_addr_with_port > test_v6_linklocal_addr_with_port)); REQUIRE(test_v6_linklocal_addr_with_port != default_addr); REQUIRE(test_v6_linklocal_addr_with_port != test_v4_addr); REQUIRE(test_v6_linklocal_addr_with_port != test_v4_addr_with_port); @@ -631,6 +662,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_any_addr.flow_info() == 0); REQUIRE(test_v4_any_addr == test_v4_any_addr); + REQUIRE(!(test_v4_any_addr != test_v4_any_addr)); + REQUIRE(!(test_v4_any_addr < test_v4_any_addr)); + REQUIRE(!(test_v4_any_addr > test_v4_any_addr)); REQUIRE(test_v4_any_addr != default_addr); REQUIRE(test_v4_any_addr != test_v4_addr); REQUIRE(test_v4_any_addr != test_v4_addr_with_port); @@ -656,6 +690,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v4_any_addr_with_port.flow_info() == 0); REQUIRE(test_v4_any_addr_with_port == test_v4_any_addr_with_port); + REQUIRE(!(test_v4_any_addr_with_port != test_v4_any_addr_with_port)); + REQUIRE(!(test_v4_any_addr_with_port < test_v4_any_addr_with_port)); + REQUIRE(!(test_v4_any_addr_with_port > test_v4_any_addr_with_port)); REQUIRE(test_v4_any_addr_with_port != default_addr); REQUIRE(test_v4_any_addr_with_port != test_v4_addr); REQUIRE(test_v4_any_addr_with_port != test_v4_addr_with_port); @@ -681,6 +718,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_any_addr.flow_info() == 0); REQUIRE(test_v6_any_addr == test_v6_any_addr); + REQUIRE(!(test_v6_any_addr != test_v6_any_addr)); + REQUIRE(!(test_v6_any_addr < test_v6_any_addr)); + REQUIRE(!(test_v6_any_addr > test_v6_any_addr)); REQUIRE(test_v6_any_addr != default_addr); REQUIRE(test_v6_any_addr != test_v4_addr); REQUIRE(test_v6_any_addr != test_v4_addr_with_port); @@ -708,6 +748,9 @@ TEST_CASE("NetworkingTests::Verifying_in_addr_interactions", "[networking]") REQUIRE(test_v6_any_addr_with_port.flow_info() == 0); REQUIRE(test_v6_any_addr_with_port == test_v6_any_addr_with_port); + REQUIRE(!(test_v6_any_addr_with_port != test_v6_any_addr_with_port)); + REQUIRE(!(test_v6_any_addr_with_port < test_v6_any_addr_with_port)); + REQUIRE(!(test_v6_any_addr_with_port > test_v6_any_addr_with_port)); REQUIRE(test_v6_any_addr_with_port != default_addr); REQUIRE(test_v6_any_addr_with_port != test_v4_addr); REQUIRE(test_v6_any_addr_with_port != test_v4_addr_with_port); @@ -731,8 +774,7 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") using wil::network::socket_address; SECTION("verify v4 address comparisons") { - // equal will be considered less-than - REQUIRE(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.1"}); + REQUIRE(!(socket_address{L"1.1.1.1"} < socket_address{L"1.1.1.1"})); REQUIRE(!(socket_address{L"1.1.1.1"} > socket_address{L"1.1.1.1"})); REQUIRE(socket_address{L"1.1.1.1"} == socket_address{L"1.1.1.1"}); REQUIRE(!(socket_address{L"1.1.1.1"} != socket_address{L"1.1.1.1"})); @@ -770,8 +812,7 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") SECTION("verify v6 address comparisons") { - // equal will be considered less-than - REQUIRE(socket_address{L"2001::1002"} < socket_address{L"2001::1002"}); + REQUIRE(!(socket_address{L"2001::1002"} < socket_address{L"2001::1002"})); REQUIRE(!(socket_address{L"2001::1002"} > socket_address{L"2001::1002"})); REQUIRE(socket_address{L"2001::1002"} == socket_address{L"2001::1002"}); REQUIRE(!(socket_address{L"2001::1002"} != socket_address{L"2001::1002"})); @@ -800,6 +841,7 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") lhs_scope_id_test.set_scope_id(10000); socket_address rhs_scope_id_test{L"2001::1002", 1}; rhs_scope_id_test.set_scope_id(100000); + REQUIRE(lhs_scope_id_test != rhs_scope_id_test); REQUIRE(lhs_scope_id_test < rhs_scope_id_test); REQUIRE(!(lhs_scope_id_test > rhs_scope_id_test)); REQUIRE(lhs_scope_id_test != rhs_scope_id_test); @@ -809,6 +851,7 @@ TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") lhs_flow_info_test.set_flow_info(10000); socket_address rhs_flow_info_test{L"2001::1002", 1}; rhs_flow_info_test.set_flow_info(100000); + REQUIRE(lhs_flow_info_test != rhs_flow_info_test); REQUIRE(lhs_flow_info_test < rhs_flow_info_test); REQUIRE(!(lhs_flow_info_test > rhs_flow_info_test)); REQUIRE(lhs_flow_info_test != rhs_flow_info_test); From f0026554e9860976bdd1cbb349c30af701729556 Mon Sep 17 00:00:00 2001 From: Keith Horton Date: Sat, 25 Jan 2025 18:44:58 -0800 Subject: [PATCH 22/22] Fixing iterators. --- include/wil/network.h | 19 +++-- include/wil/resource.h | 8 +- tests/NetworkingTests.cpp | 172 ++++++++++++++++++++++---------------- 3 files changed, 117 insertions(+), 82 deletions(-) diff --git a/include/wil/network.h b/include/wil/network.h index c0923276..4df0ca88 100644 --- a/include/wil/network.h +++ b/include/wil/network.h @@ -302,11 +302,6 @@ namespace network class addr_info_iterator_t { public: - static constexpr addr_info_iterator_t end() WI_NOEXCEPT - { - return addr_info_iterator_t{}; - } - // defining iterator_traits allows STL functions to be used with this iterator class. // Notice this is a forward_iterator // - does not support random-access (e.g. vector::iterator) @@ -410,6 +405,20 @@ namespace network ::wil::network::socket_address m_socket_address{}; }; // class addr_info_iterator_t + // begin() and end() support - enabling range-based for loop + template + constexpr + addr_info_iterator_t end(addr_info_iterator_t) WI_NOEXCEPT + { + return {}; + } + + template + addr_info_iterator_t begin(addr_info_iterator_t addrinfo) WI_NOEXCEPT + { + return addrinfo; + } + using addr_info_ansi_iterator = addr_info_iterator_t; using addr_info_iterator = addr_info_iterator_t; // not defining a type for ADDRINFOEXA as that type is formally __declspec(deprecated) diff --git a/include/wil/resource.h b/include/wil/resource.h index 1d88b5db..8da1af7a 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5089,10 +5089,10 @@ typedef weak_any weak_socket; /// @cond #define __WIL_WS2TCPIP_H_ /// @endcond -typedef unique_any unique_addrinfo_ansi; -typedef unique_any unique_addrinfo; -// not defining a type for FreeAddrInfoEx(PADDRINFOEXA) as that API is formally __declspec(deprecated) -typedef unique_any unique_addrinfoex; +typedef unique_any unique_addrinfo_ansi; +typedef unique_any unique_addrinfo; +// not defining a type for FreeAddrInfoEx(ADDRINFOEXA*) as that API is formally __declspec(deprecated) +typedef unique_any unique_addrinfoex; #endif // __WIL_WS2TCPIP_H_ #if (defined(__WIL_WS2TCPIP_H_) && !defined(__WIL_WS2TCPIP_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) /// @cond diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp index 498746f1..4dd7b063 100644 --- a/tests/NetworkingTests.cpp +++ b/tests/NetworkingTests.cpp @@ -2123,6 +2123,8 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") using wil::network::addr_info_ansi_iterator; using wil::network::addr_info_iterator; using wil::network::addr_infoex_iterator; + using wil::network::begin; + using wil::network::end; using wil::network::equals; const auto cleanup = wil::network::WSAStartup_nothrow(); @@ -2131,27 +2133,29 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify resolve_local_addresses") { #if (defined(WIL_ENABLE_EXCEPTIONS)) - const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); - REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); + { + const wil::unique_addrinfo test_addr = wil::network::resolve_local_addresses(); + REQUIRE(begin(addr_info_iterator{test_addr.get()}) != end(addr_info_iterator{})); - // verify operator-> - const addr_info_iterator test_addr_iterator{test_addr.get()}; - REQUIRE(!test_addr_iterator->is_address_loopback()); + // verify operator-> + const addr_info_iterator test_addr_iterator{test_addr.get()}; + REQUIRE(!test_addr_iterator->is_address_loopback()); - uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) - { - const auto family = address.family(); - REQUIRE((family == AF_INET || family == AF_INET6)); - REQUIRE(!address.is_address_loopback()); + uint32_t count = 0; + for (const auto& address : addr_info_iterator(test_addr.get())) + { + const auto family = address.family(); + REQUIRE((family == AF_INET || family == AF_INET6)); + REQUIRE(!address.is_address_loopback()); + + wil::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... resolve_local_addresses : %ws\n", address_string); + ++count; + } - wil::network::socket_address_wstring address_string; - REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); - wprintf(L"... resolve_local_addresses : %ws\n", address_string); - ++count; + REQUIRE(count > 0); } - - REQUIRE(count > 0); #endif } @@ -2159,14 +2163,14 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { #if (defined(WIL_ENABLE_EXCEPTIONS)) const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); + REQUIRE(begin(addr_info_iterator{test_addr.get()}) != end(addr_info_iterator{})); // verify operator-> const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(test_addr_iterator->is_address_loopback()); uint32_t count = 0; - for (const auto& address : wil::make_range(addr_info_iterator{test_addr.get()}, addr_info_iterator::end())) + for (const auto& address : wil::make_range(begin(addr_info_iterator{test_addr.get()}), end(addr_info_iterator{}))) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2198,51 +2202,62 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") SECTION("verify const addr_info_iterator") { -#if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) - const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); + constexpr auto* local_address_name_string = L"localhost"; + ADDRINFOW* addrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) == 0); + + const wil::unique_addrinfo test_addr{addrResult}; + REQUIRE(begin(addr_info_iterator{test_addr.get()}) != end(addr_info_iterator{})); const addr_info_iterator test_addr_iterator{test_addr.get()}; REQUIRE(test_addr_iterator->is_address_loopback()); const auto& test_address_reference = *test_addr_iterator; REQUIRE(test_address_reference.is_address_loopback()); -#endif } SECTION("verify addr_info_iterator increment") { -#if (defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_)) - const wil::unique_addrinfo initial_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(addr_info_iterator{initial_addr.get()} != addr_info_iterator::end()); + constexpr auto* local_address_name_string = L"localhost"; + ADDRINFOW* addrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) == 0); + + const wil::unique_addrinfo initial_addr{addrResult}; + REQUIRE(begin(addr_info_iterator{initial_addr.get()}) != end(addr_info_iterator{})); auto total_count = 0; - for (auto it = addr_info_iterator{initial_addr.get()}; it != addr_info_iterator::end(); ++it) + for (auto it = begin(addr_info_iterator{initial_addr.get()}); it != end(addr_info_iterator{}); ++it) { ++total_count; } - const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_addresses(); - REQUIRE(addr_info_iterator{test_addr.get()} != addr_info_iterator::end()); + ADDRINFOW* testAddrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &testAddrResult) == 0); + const wil::unique_addrinfo test_addr{testAddrResult}; + REQUIRE(addr_info_iterator{test_addr.get()} != end(addr_info_iterator{})); addr_info_iterator test_iterator = addr_info_iterator{test_addr.get()}; test_iterator += total_count; - REQUIRE(test_iterator == addr_info_iterator::end()); -#endif + REQUIRE(test_iterator == end(addr_info_iterator{})); } SECTION("verify addr_info_iterator move behavior") { -#if (defined(WIL_ENABLE_EXCEPTIONS)) - wil::unique_addrinfo moved_from_addr = wil::network::resolve_local_addresses(); - REQUIRE(addr_info_iterator{moved_from_addr.get()} != addr_info_iterator::end()); + constexpr auto* local_address_name_string = L""; + ADDRINFOW* addrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) == 0); + + wil::unique_addrinfo moved_from_addr{addrResult}; + REQUIRE(begin(addr_info_iterator{moved_from_addr.get()}) != end(addr_info_iterator{})); const wil::unique_addrinfo moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); + // moved_from_addr should be end() now - REQUIRE(addr_info_iterator{moved_from_addr.get()} == addr_info_iterator::end()); - REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_from_addr.get()} == end(addr_info_iterator{})); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != end(addr_info_iterator{})); - for (const auto& address : wil::make_range(addr_info_iterator{moved_to_addr.get()}, addr_info_iterator::end())) + for (const auto& address : wil::make_range(addr_info_iterator{moved_to_addr.get()}, end(addr_info_iterator{}))) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2252,27 +2267,33 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); wprintf(L"... moved resolve_local_addresses : %ws\n", address_string); } -#endif } SECTION("verify addr_info_iterator move assignment behavior") { -#if (defined(WIL_ENABLE_EXCEPTIONS)) - wil::unique_addrinfo moved_from_addr = wil::network::resolve_local_addresses(); - REQUIRE(addr_info_iterator{moved_from_addr.get()} != addr_info_iterator::end()); + constexpr auto* local_address_name_string = L""; + ADDRINFOW* addrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) == 0); + + wil::unique_addrinfo moved_from_addr{addrResult}; + REQUIRE(begin(addr_info_iterator{moved_from_addr.get()}) != end(addr_info_iterator{})); + + ADDRINFOW* moveToAddrResult{}; + REQUIRE(::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &moveToAddrResult) == 0); - wil::unique_addrinfo moved_to_addr{wil::network::resolve_local_addresses()}; + wil::unique_addrinfo moved_to_addr{moveToAddrResult}; moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); // moved_from_addr should be end() now - REQUIRE(addr_info_iterator{moved_from_addr.get()} == addr_info_iterator::end()); - REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_from_addr.get()} == end(addr_info_iterator{})); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != end(addr_info_iterator{})); // move to self moved_to_addr = std::move(moved_to_addr); - REQUIRE(addr_info_iterator{moved_to_addr.get()} != addr_info_iterator::end()); + REQUIRE(addr_info_iterator{moved_to_addr.get()} != end(addr_info_iterator{})); - for (const auto& address : wil::make_range(addr_info_iterator{moved_to_addr.get()}, addr_info_iterator::end())) + for (const auto& address : addr_info_iterator{moved_to_addr.get()}) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2282,7 +2303,6 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); wprintf(L"... move assignment resolve_local_addresses : %ws\n", address_string); } -#endif } // retest with unique_addrinfo_ansi @@ -2290,35 +2310,37 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { wil::unique_addrinfo_ansi initial_addr; REQUIRE(0 == getaddrinfo("localhost", nullptr, nullptr, initial_addr.addressof())); - REQUIRE(addr_info_ansi_iterator{initial_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{initial_addr.get()} != end(addr_info_ansi_iterator{})); auto total_count = 0; - for (auto it = addr_info_ansi_iterator{initial_addr.get()}; it != addr_info_ansi_iterator::end(); ++it) + for (auto it = begin(addr_info_ansi_iterator{initial_addr.get()}); it != end(addr_info_ansi_iterator{}); ++it) { ++total_count; } wil::unique_addrinfo_ansi test_addr; REQUIRE(0 == getaddrinfo("localhost", nullptr, nullptr, test_addr.addressof())); - REQUIRE(addr_info_ansi_iterator{test_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{test_addr.get()} != end(addr_info_ansi_iterator{})); addr_info_ansi_iterator test_iterator = addr_info_ansi_iterator{test_addr.get()}; test_iterator += total_count; - REQUIRE(test_iterator == addr_info_ansi_iterator::end()); + REQUIRE(test_iterator == end(addr_info_ansi_iterator{})); } SECTION("verify addr_info_ansi_iterator move behavior") { wil::unique_addrinfo_ansi moved_from_addr; REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_from_addr.addressof())); - REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != end(addr_info_ansi_iterator{})); const wil::unique_addrinfo_ansi moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); + // moved_from_addr should be end() now - REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == addr_info_ansi_iterator::end()); - REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == end(addr_info_ansi_iterator{})); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != end(addr_info_ansi_iterator{})); - for (const auto& address : wil::make_range(addr_info_ansi_iterator{moved_to_addr.get()}, addr_info_ansi_iterator::end())) + for (const auto& address : wil::make_range(begin(addr_info_ansi_iterator{moved_to_addr.get()}), end(addr_info_ansi_iterator{}))) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2334,21 +2356,22 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { wil::unique_addrinfo_ansi moved_from_addr; REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_from_addr.addressof())); - REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} != end(addr_info_ansi_iterator{})); wil::unique_addrinfo_ansi moved_to_addr; REQUIRE(0 == getaddrinfo("", nullptr, nullptr, moved_to_addr.addressof())); moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); // moved_from_addr should be end() now - REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == addr_info_ansi_iterator::end()); - REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_from_addr.get()} == end(addr_info_ansi_iterator{})); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != end(addr_info_ansi_iterator{})); // move to self moved_to_addr = std::move(moved_to_addr); - REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != addr_info_ansi_iterator::end()); + REQUIRE(addr_info_ansi_iterator{moved_to_addr.get()} != end(addr_info_ansi_iterator{})); - for (const auto& address : wil::make_range(addr_info_ansi_iterator{moved_to_addr.get()}, addr_info_ansi_iterator::end())) + for (const auto& address : addr_info_ansi_iterator{moved_to_addr.get()}) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2365,35 +2388,37 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { wil::unique_addrinfoex initial_addr; REQUIRE(0 == GetAddrInfoExW(L"localhost", nullptr, NS_ALL, nullptr, nullptr, initial_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); - REQUIRE(addr_infoex_iterator{initial_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(begin(addr_infoex_iterator{initial_addr.get()}) != end(addr_infoex_iterator{})); auto total_count = 0; - for (auto it = addr_infoex_iterator{initial_addr.get()}; it != addr_infoex_iterator::end(); ++it) + for (auto it = begin(addr_infoex_iterator{initial_addr.get()}); it != end(addr_infoex_iterator{}); ++it) { ++total_count; } wil::unique_addrinfoex test_addr; REQUIRE(0 == GetAddrInfoExW(L"localhost", nullptr, NS_ALL, nullptr, nullptr, test_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); - REQUIRE(addr_infoex_iterator{test_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{test_addr.get()} != wil::network::end(addr_infoex_iterator{})); addr_infoex_iterator test_iterator = addr_infoex_iterator{test_addr.get()}; test_iterator += total_count; - REQUIRE(test_iterator == addr_infoex_iterator::end()); + REQUIRE(test_iterator == wil::network::end(addr_infoex_iterator{})); } SECTION("verify addr_infoex_iterator move behavior") { wil::unique_addrinfoex moved_from_addr; REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_from_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); - REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != end(addr_infoex_iterator{})); const wil::unique_addrinfoex moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); + // moved_from_addr should be end() now - REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == addr_infoex_iterator::end()); - REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == end(addr_infoex_iterator{})); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != end(addr_infoex_iterator{})); - for (const auto& address : wil::make_range(addr_infoex_iterator{moved_to_addr.get()}, addr_infoex_iterator::end())) + for (const auto& address : addr_infoex_iterator{moved_to_addr.get()}) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6)); @@ -2409,21 +2434,22 @@ TEST_CASE("NetworkingTests::Verifying_addr_info", "[networking]") { wil::unique_addrinfoex moved_from_addr; REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_from_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); - REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} != end(addr_infoex_iterator{})); wil::unique_addrinfoex moved_to_addr; REQUIRE(0 == GetAddrInfoExW(L"", nullptr, NS_ALL, nullptr, nullptr, moved_to_addr.addressof(), nullptr, nullptr, nullptr, nullptr)); moved_to_addr = std::move(moved_from_addr); + REQUIRE(moved_to_addr != moved_from_addr); // moved_from_addr should be end() now - REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == addr_infoex_iterator::end()); - REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_from_addr.get()} == end(addr_infoex_iterator{})); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != end(addr_infoex_iterator{})); // move to self moved_to_addr = std::move(moved_to_addr); - REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != addr_infoex_iterator::end()); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != end(addr_infoex_iterator{})); - for (const auto& address : wil::make_range(addr_infoex_iterator{moved_to_addr.get()}, addr_infoex_iterator::end())) + for (const auto& address : wil::make_range(begin(addr_infoex_iterator{moved_to_addr.get()}), end(addr_infoex_iterator{}))) { const auto family = address.family(); REQUIRE((family == AF_INET || family == AF_INET6));