diff --git a/include/wil/common.h b/include/wil/common.h index ec420e941..23ec2cc61 100644 --- a/include/wil/common.h +++ b/include/wil/common.h @@ -842,7 +842,7 @@ namespace details : sizeof(val) == 2 ? static_cast(val) \ : sizeof(val) == 4 ? static_cast(val) \ : static_cast(val)) __pragma(warning(pop))) -#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val)-1))) +#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1))) #define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val)) template diff --git a/include/wil/network.h b/include/wil/network.h new file mode 100644 index 000000000..7af5a9b3b --- /dev/null +++ b/include/wil/network.h @@ -0,0 +1,1320 @@ +//********************************************************* +// +// 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 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 + +#ifdef _KERNEL_MODE +#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) + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +// required wil headers +#include "wistd_type_traits.h" +#include "resource.h" + +namespace wil +{ +// 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 + 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 ::wil::network::unique_wsacleanup_call WSAStartup_nothrow() WI_NOEXCEPT + { + WSADATA unused_data{}; + const auto error{::WSAStartup(WINSOCK_VERSION, &unused_data)}; + LOG_IF_WIN32_ERROR(error); + + ::wil::network::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 WSAStartup succeeded + return_cleanup.release(); + } + return return_cleanup; + } + + // 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{}; + FAIL_FAST_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); + return {}; + } + +#if defined(WIL_ENABLE_EXCEPTIONS) + // Calls WSAStartup and throws on error; returns an RAII object that reverts + WI_NODISCARD inline ::wil::network::unique_wsacleanup_call WSAStartup() + { + WSADATA unused_data{}; + THROW_IF_WIN32_ERROR(::WSAStartup(WINSOCK_VERSION, &unused_data)); + return {}; + } +#endif + + // + // 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 !::wil::network::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 !::wil::network::equals(lhs, rhs); + } + + // + // 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); + typedef WCHAR socket_address_wstring[INET6_ADDRSTRLEN]; + typedef CHAR socket_address_string[INET6_ADDRSTRLEN]; + + class socket_address final + { + public: + 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; + 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*, unsigned short port = 0) WI_NOEXCEPT; + explicit socket_address(const IN6_ADDR*, unsigned short port = 0) WI_NOEXCEPT; +#if defined(WIL_ENABLE_EXCEPTIONS) + explicit socket_address(PCWSTR, unsigned short port = 0); +#endif + + ~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 (including address family) + template + 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; + void set_sockaddr(const SOCKET_ADDRESS*) 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; + + // 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; + 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; + + 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; + + // 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; + HRESULT write_complete_address_nothrow(socket_address_string& address) const WI_NOEXCEPT; + +#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 + + // 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; + + [[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]] SOCKADDR_STORAGE sockaddr_storage() const WI_NOEXCEPT; + + [[nodiscard]] constexpr int length() const + { + return sizeof(m_sockaddr); + } + + 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 ::wil::network::socket_address map_dual_mode_4to6(const ::wil::network::socket_address& ipv4_address) WI_NOEXCEPT + { + constexpr IN6_ADDR ipv4MappedPrefix{{IN6ADDR_V4MAPPEDPREFIX_INIT}}; + + ::wil::network::socket_address return_ipv6_address{&ipv4MappedPrefix, ipv4_address.port()}; + + 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 return_ipv6_address; + } + + // 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 + // ! template T supports pointers to the 3 address structures: ADDRINFOA*, ADDRINFOW*, ADDRINFOEXW* + template + class addr_info_iterator_t + { + 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 WIL_USE_STL || defined(WIL_DOXYGEN) + 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&; + + 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 + m_addrinfo_ptr(const_cast(addrinfo)) + { + if (m_addrinfo_ptr) + { + m_socket_address.set_sockaddr(m_addrinfo_ptr->ai_addr, m_addrinfo_ptr->ai_addrlen); + } + } + + ~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; + + 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 addr_info_iterator_t& rhs) const WI_NOEXCEPT + { + return m_addrinfo_ptr == rhs.m_addrinfo_ptr; + } + + [[nodiscard]] bool operator!=(const addr_info_iterator_t& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + addr_info_iterator_t& operator++() WI_NOEXCEPT + { + this->operator+=(1); + return *this; + } + + addr_info_iterator_t operator++(int) WI_NOEXCEPT + { + auto return_value = *this; + this->operator+=(1); + return return_value; + } + + addr_info_iterator_t& operator+=(size_t offset) WI_NOEXCEPT + { + 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 + + // 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) + 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() + { + constexpr auto* local_address_name_string = L""; + ADDRINFOW* addrResult{}; + if (::GetAddrInfoW(local_address_name_string, nullptr, nullptr, &addrResult) != 0) + { + THROW_WIN32(::WSAGetLastError()); + } + + 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() + { + constexpr auto* localhost_address_name_string = L"localhost"; + ADDRINFOW* addrResult{}; + if (::GetAddrInfoW(localhost_address_name_string, nullptr, nullptr, &addrResult) != 0) + { + THROW_WIN32(::WSAGetLastError()); + } + + return ::wil::unique_addrinfo{addrResult}; + } +#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; + } + + // + // socket_address definitions + // + inline socket_address::socket_address(ADDRESS_FAMILY family) WI_NOEXCEPT + { + reset(family); + } + + template + socket_address::socket_address(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT + { + set_sockaddr(addr, addr_size); + } + + 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); + } + + inline socket_address::socket_address(const IN_ADDR* addr, unsigned short port) WI_NOEXCEPT + { + reset(AF_INET); + set_address(addr); + set_port(port); + } + + inline socket_address::socket_address(const IN6_ADDR* addr, unsigned short port) WI_NOEXCEPT + { + reset(AF_INET6); + set_address(addr); + set_port(port); + } + +#if defined(WIL_ENABLE_EXCEPTIONS) + inline socket_address::socket_address(PCWSTR addr, unsigned short port) + { + set_sockaddr(addr); + set_port(port); + } +#endif + inline bool socket_address::operator==(const ::wil::network::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 ::wil::network::socket_address& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + inline bool socket_address::operator<(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + { + const auto& lhs{*this}; + + if (lhs.family() != rhs.family()) + { + return lhs.family() < rhs.family(); + } + + // 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 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) + // 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; + } + + // must be exactly equal, so not less-than + return false; + } + + case AF_INET6: + { + // 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; + } + + // must be exactly equal, so not less-than + return false; + } + + 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; + } + } + + inline bool socket_address::operator>(const ::wil::network::socket_address& rhs) const WI_NOEXCEPT + { + if (*this == rhs) + { + return false; + } + return !(*this < rhs); + } + + 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)); + ::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 + { +#if (!defined(WI_NETWORK_TEST)) + WI_ASSERT(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); +#endif + ::memset(&m_sockaddr, 0, length()); + m_sockaddr.si_family = family; + } + + template + void socket_address::set_sockaddr(_In_reads_bytes_(addr_size) const SOCKADDR* addr, T addr_size) WI_NOEXCEPT + { + WI_ASSERT(static_cast(addr_size) <= static_cast(length())); + + ::memset(&m_sockaddr, 0, length()); + if (addr) + { + ::memcpy_s(&m_sockaddr, sizeof(m_sockaddr), addr, addr_size); + } + } + + inline void socket_address::set_sockaddr(const SOCKADDR_IN* addr) WI_NOEXCEPT + { + ::memset(&m_sockaddr, 0, 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 + { + ::memset(&m_sockaddr, 0, 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 + { + ::memset(&m_sockaddr, 0, 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 > length(), + "SOCKET_ADDRESS contains an unsupported sockaddr type - larger than an IPv4 or IPv6 address (%d)", + addr->iSockaddrLength); + + ::memset(&m_sockaddr, 0, 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 + { + 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()); + + default: + 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()); + + default: + WI_ASSERT_MSG(false, "Unknown address family"); + return false; + } + } + + inline NL_ADDRESS_TYPE socket_address::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())); + + 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); + } + + 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 = ::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 = ::htonl(flowInfo); + } + } + + inline void socket_address::set_address_any() WI_NOEXCEPT + { + set_address_any(family()); + } + + 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}; + reset(family); + m_sockaddr.Ipv4.sin_port = original_port; + } + + inline void socket_address::set_address_loopback() WI_NOEXCEPT + { + set_address_loopback(family()); + } + + 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: + m_sockaddr.Ipv4.sin_addr.s_addr = ::htonl(INADDR_LOOPBACK); + break; + case AF_INET6: + m_sockaddr.Ipv6.sin6_addr = {{IN6ADDR_LOOPBACK_INIT}}; + break; + default: + WI_ASSERT_MSG(false, "Unknown address 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}; + 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 + { + 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; + } + +#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 error{::getsockname(s, sockaddr(), &nameLength)}; + if (error != 0) + { + error = ::WSAGetLastError(); + RETURN_WIN32(error); + } + return S_OK; + } + + inline HRESULT socket_address::set_sockaddr_nothrow(PCWSTR address) WI_NOEXCEPT + { + PCWSTR terminator_unused; + + reset(AF_INET); + constexpr BOOLEAN strict_string{TRUE}; + if (RtlIpv4StringToAddressW(address, strict_string, &terminator_unused, in_addr()) == 0) + { + m_sockaddr.si_family = AF_INET; + return S_OK; + } + + 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_sockaddr_nothrow(PCSTR address) WI_NOEXCEPT + { + PCSTR terminator_unused; + + reset(AF_INET); + constexpr BOOLEAN strict_string{TRUE}; + if (RtlIpv4StringToAddressA(address, strict_string, &terminator_unused, in_addr()) == 0) + { + m_sockaddr.si_family = AF_INET; + return S_OK; + } + + 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 (WIL_USE_STL && defined(WIL_ENABLE_EXCEPTIONS)) || defined(WIL_DOXYGEN) + inline ::std::wstring socket_address::write_address() const + { + ::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 + { + ::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)}; + + // the last param to InetNtopW is # of characters, not bytes + const auto* error_value{::InetNtopW(family(), pAddr, address, INET6_ADDRSTRLEN)}; + if (error_value == nullptr) + { + RETURN_WIN32(::WSAGetLastError()); + } + return S_OK; + } + + inline HRESULT 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)}; + + // the last param to InetNtopA is # of characters, not bytes + const auto* error_value{::InetNtopA(family(), pAddr, address, INET6_ADDRSTRLEN)}; + if (error_value == nullptr) + { + RETURN_WIN32(::WSAGetLastError()); + } + return S_OK; + } + +#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{}; + 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 + { + ::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()), length(), nullptr, address, &addressLength) != 0) + { + 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 + { + ::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()), length(), nullptr, address, &addressLength) != 0) + { + RETURN_WIN32(::WSAGetLastError()); + } + 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: + // fallthrough + case AF_INET: + return 0; + case AF_INET6: + return ::ntohl(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: + // fallthrough + case AF_INET: + return 0; + case AF_INET6: + return ::ntohl(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 + { + 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; + } + + inline SOCKADDR_INET* socket_address::sockaddr_inet() WI_NOEXCEPT + { + return &m_sockaddr; + } + + 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; + } + + 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 + { + 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; + } + + 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 + { + 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; + } + + 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 + +#endif // __WIL_NETWORK_INCLUDED \ No newline at end of file diff --git a/include/wil/nt_result_macros.h b/include/wil/nt_result_macros.h index 627061f52..2682382df 100644 --- a/include/wil/nt_result_macros.h +++ b/include/wil/nt_result_macros.h @@ -138,11 +138,11 @@ _Always_(_Post_satisfies_(return < 0)) __declspec(noinline) inline NTSTATUS Stat namespace details { template - __declspec(noinline) inline NTSTATUS - ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException( + __R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); template - __declspec(noinline) inline NTSTATUS - ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg( + __R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); namespace __R_NS_NAME { @@ -191,8 +191,7 @@ namespace details } template - __declspec(noinline) inline NTSTATUS - ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { // Pre-populate the buffer with our message, the exception message will be added to it... wchar_t message[2048]; diff --git a/include/wil/registry_helpers.h b/include/wil/registry_helpers.h index a9985d060..76b5d247b 100644 --- a/include/wil/registry_helpers.h +++ b/include/wil/registry_helpers.h @@ -1060,7 +1060,7 @@ namespace reg { return REG_SZ; } -#endif // #if defined(__WIL_OBJBASE_H_STL) +#endif // #if defined(__WIL_OBJBASE_H_STL) } // namespace reg_value_type_info template @@ -1294,8 +1294,8 @@ namespace reg #if defined(WIL_ENABLE_EXCEPTIONS) using reg_view = ::wil::reg::reg_view_details::reg_view_t<::wil::err_exception_policy>; #endif // #if defined(WIL_ENABLE_EXCEPTIONS) - } // namespace reg_view_details - /// @endcond + } // namespace reg_view_details + /// @endcond /// @cond namespace reg_iterator_details @@ -1426,8 +1426,8 @@ namespace reg return ::wil::unique_bstr{::SysAllocStringLen(name.get(), static_cast(length))}; } #endif // #if defined(__WIL_OLEAUTO_H_) - } // namespace reg_iterator_details - /// @endcond + } // namespace reg_iterator_details + /// @endcond // forward declaration to allow friend-ing the template iterator class #if defined(WIL_ENABLE_EXCEPTIONS) diff --git a/include/wil/resource.h b/include/wil/resource.h index 193ea9294..30daf3407 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -5085,6 +5085,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(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 +#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/include/wil/result.h b/include/wil/result.h index 178fde36e..65c719e1d 100644 --- a/include/wil/result.h +++ b/include/wil/result.h @@ -1166,8 +1166,8 @@ inline void WilInitialize_Result(WilInitializeCommand state) namespace details { #ifndef RESULT_SUPPRESS_STATIC_INITIALIZERS - __declspec(selectany)::wil::details_abi::ProcessLocalStorage<::wil::details_abi::ProcessLocalData> g_processLocalData("WilError_03"); - __declspec(selectany)::wil::details_abi::ThreadLocalStorage g_threadFailureCallbacks; + __declspec(selectany) ::wil::details_abi::ProcessLocalStorage<::wil::details_abi::ProcessLocalData> g_processLocalData("WilError_03"); + __declspec(selectany) ::wil::details_abi::ThreadLocalStorage g_threadFailureCallbacks; WI_HEADER_INITIALIZATION_FUNCTION(InitializeResultHeader, [] { g_pfnGetContextAndNotifyFailure = GetContextAndNotifyFailure; diff --git a/include/wil/result_macros.h b/include/wil/result_macros.h index 66ab5381d..ca72f28ed 100644 --- a/include/wil/result_macros.h +++ b/include/wil/result_macros.h @@ -1991,8 +1991,7 @@ namespace details __declspec(selectany) void(__stdcall* g_pfnRaiseFailFastException)(PEXCEPTION_RECORD, PCONTEXT, DWORD) = nullptr; // Exception-based compiled additions - __declspec(selectany) - HRESULT(__stdcall* g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr; + __declspec(selectany) HRESULT(__stdcall* g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr; __declspec(selectany) void(__stdcall* g_pfnRethrow)() = nullptr; __declspec(selectany) void(__stdcall* g_pfnThrowResultException)(const FailureInfo& failure) = nullptr; extern "C" __declspec(selectany) ResultStatus(__stdcall* g_pfnResultFromCaughtExceptionInternal)( @@ -2213,8 +2212,8 @@ namespace details template __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags flags = FailureFlags::None); template - __declspec(noinline) inline HRESULT - ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + __declspec(noinline) inline HRESULT ReportFailure_CaughtException( + __R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); //***************************************************************************** // Fail fast helpers (for use only internally to WIL) @@ -3133,7 +3132,7 @@ namespace details g_pfnRaiseFailFastException = ::RaiseFailFastException; return 1; }); -} +} // namespace details #endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) //***************************************************************************** @@ -4119,8 +4118,7 @@ namespace details } } - __declspec(noinline) inline HRESULT - ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + __declspec(noinline) inline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT { #ifdef RESULT_DEBUG // We can't do debug SEH handling if the caller also wants a shot at mapping the exceptions @@ -4140,8 +4138,8 @@ namespace details } } - __declspec(noinline) inline HRESULT - ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + __declspec(noinline) inline HRESULT ResultFromExceptionDebug( + const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT { return wil::details::ResultFromExceptionSeh(diagnostics, _ReturnAddress(), supported, functor); } @@ -4579,8 +4577,8 @@ namespace details } template - inline __declspec(noinline) RESULT_NORETURN - void ReportFailure_NoReturn(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_NoReturn( + __R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) { bool needPlatformException = ((T == FailureType::Exception) && WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && @@ -4814,15 +4812,13 @@ namespace details } template <> - __declspec(noinline) inline RESULT_NORETURN - void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) { ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); } template <> - __declspec(noinline) inline RESULT_NORETURN - void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) { ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); } @@ -4973,7 +4969,7 @@ namespace details template <> __declspec(noinline) inline RESULT_NORETURN HRESULT - ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) { wchar_t message[2048]{}; RESULT_NORETURN_RESULT( @@ -4982,7 +4978,7 @@ namespace details template <> __declspec(noinline) inline RESULT_NORETURN HRESULT - ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) { wchar_t message[2048]{}; RESULT_NORETURN_RESULT( @@ -4996,15 +4992,15 @@ namespace details } template <> - __declspec(noinline) inline RESULT_NORETURN - void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg( + __R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) { ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); } template <> - __declspec(noinline) inline RESULT_NORETURN - void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg( + __R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) { ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); } @@ -5012,8 +5008,7 @@ namespace details template _Success_(true) _Translates_Win32_to_HRESULT_(err) - __declspec(noinline) inline HRESULT - ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) { auto hr = __HRESULT_FROM_WIN32(err); ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); @@ -5043,8 +5038,7 @@ namespace details } template - __declspec(noinline) inline DWORD - ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { auto err = GetLastErrorFail(__R_FN_CALL_FULL); auto hr = __HRESULT_FROM_WIN32(err); @@ -5054,8 +5048,8 @@ namespace details } template <> - __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg( - __R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline RESULT_NORETURN DWORD + ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { auto err = GetLastErrorFail(__R_FN_CALL_FULL); auto hr = __HRESULT_FROM_WIN32(err); @@ -5076,8 +5070,7 @@ namespace details template _Success_(true) _Translates_last_error_to_HRESULT_ - __declspec(noinline) inline HRESULT - ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); @@ -5109,8 +5102,8 @@ namespace details template _Success_(true) _Translates_NTSTATUS_to_HRESULT_(status) - __declspec(noinline) inline HRESULT - ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline HRESULT ReportFailure_NtStatusMsg( + __R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) { const auto resultPair = ResultStatus::FromStatus(status); ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); @@ -5140,8 +5133,7 @@ namespace details } template - __declspec(noinline) inline HRESULT - ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + __declspec(noinline) inline HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { // Pre-populate the buffer with our message, the exception message will be added to it... wchar_t message[2048]; @@ -5250,8 +5242,8 @@ namespace details } template - __declspec(noinline) RESULT_NORETURN - inline void ReportFailure_CustomExceptionMsg(__R_FN_PARAMS _In_ T exception, _In_ _Printf_format_string_ PCSTR formatString, ...) + __declspec(noinline) RESULT_NORETURN inline void ReportFailure_CustomExceptionMsg( + __R_FN_PARAMS _In_ T exception, _In_ _Printf_format_string_ PCSTR formatString, ...) { va_list argList; va_start(argList, formatString); @@ -5525,8 +5517,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_IfNullAlloc) - (__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT { if (pointer == nullptr) { @@ -5567,8 +5558,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_HrIfNull) - (__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT { if (pointer == nullptr) { @@ -5609,8 +5599,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_GetLastErrorIfNull) - (__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT { if (pointer == nullptr) { @@ -5789,8 +5778,8 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_IfNullAllocMsg) - (__R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_IfNullAllocMsg)( + __R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -5839,8 +5828,8 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_HrIfNullMsg) - (__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_HrIfNullMsg)( + __R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -5888,8 +5877,8 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_GetLastErrorIfNullMsg) - (__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_GetLastErrorIfNullMsg)( + __R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -6048,8 +6037,7 @@ namespace details // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc) - (__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) { if (pointer == nullptr) { @@ -6092,8 +6080,7 @@ namespace details // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull) - (__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -6136,8 +6123,7 @@ namespace details // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull) - (__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -6323,8 +6309,8 @@ namespace details } template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullAllocMsg) - (__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullAllocMsg)( + __RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -6379,8 +6365,8 @@ namespace details } template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_HrIfNullMsg) - (__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_HrIfNullMsg)( + __RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -6435,8 +6421,8 @@ namespace details } template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNullMsg) - (__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNullMsg)( + __RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -6577,8 +6563,8 @@ namespace details } template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullMsg) - (__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullMsg)( + __RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT { if (pointer == nullptr) { @@ -6648,8 +6634,7 @@ namespace details // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull) - (_In_opt_ const PointerT& pointer) + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -6798,8 +6783,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_IfNullAlloc) - (__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) { if (pointer == nullptr) { @@ -6840,8 +6824,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_HrIfNull) - (__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -6892,8 +6875,7 @@ namespace details } template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNull) - (__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { diff --git a/include/wil/win32_helpers.h b/include/wil/win32_helpers.h index c68891ced..b4f73db90 100644 --- a/include/wil/win32_helpers.h +++ b/include/wil/win32_helpers.h @@ -200,7 +200,7 @@ namespace filetime_duration long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL -}; // namespace filetime_duration +}; // namespace filetime_duration namespace filetime { diff --git a/include/wil/winrt.h b/include/wil/winrt.h index 58da0dced..4d66e2f07 100644 --- a/include/wil/winrt.h +++ b/include/wil/winrt.h @@ -168,41 +168,43 @@ namespace details } template - static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_EQUAL; } template - static auto not_equals(LhsT&& lhs, RhsT&& rhs) - WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_EQUAL; } template - static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_LESS_THAN; } template - static auto less_equals(LhsT&& lhs, RhsT&& rhs) - WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_GREATER_THAN; } template - static auto greater(LhsT&& lhs, RhsT&& rhs) - WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_GREATER_THAN; } template - static auto greater_equals(LhsT&& lhs, RhsT&& rhs) - WI_NOEXCEPT->decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT + -> decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) { return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_LESS_THAN; } @@ -1386,7 +1388,7 @@ namespace Windows return typename wil::iterable_range::iterable_iterator(); } } // namespace Collections - } // namespace Foundation + } // namespace Foundation } // namespace Windows #if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) } // namespace ABI diff --git a/include/wil/wistd_functional.h b/include/wil/wistd_functional.h index d96d71f23..c65b6274c 100644 --- a/include/wil/wistd_functional.h +++ b/include/wil/wistd_functional.h @@ -182,7 +182,7 @@ namespace __function } template - __WI_LIBCPP_INLINE_VISIBILITY bool __not_null(_Ret _Class::*__ptr) + __WI_LIBCPP_INLINE_VISIBILITY bool __not_null(_Ret _Class::* __ptr) { return __ptr; } diff --git a/include/wil/wistd_type_traits.h b/include/wil/wistd_type_traits.h index d79211e7a..081718852 100644 --- a/include/wil/wistd_type_traits.h +++ b/include/wil/wistd_type_traits.h @@ -86,9 +86,7 @@ using conditional_t = typename conditional<_Bp, _If, _Then>::type; #endif template -struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if -{ -}; +struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if{}; template struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if { @@ -96,9 +94,7 @@ struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if }; template -struct __WI_LIBCPP_TEMPLATE_VIS enable_if -{ -}; +struct __WI_LIBCPP_TEMPLATE_VIS enable_if{}; template struct __WI_LIBCPP_TEMPLATE_VIS enable_if { @@ -1194,9 +1190,7 @@ using type_identity_t = typename type_identity<_Tp>::type; // is_signed template ::value> -struct __libcpp_is_signed_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(-1) < _Tp(0)) -{ -}; +struct __libcpp_is_signed_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(-1) < _Tp(0)){}; template struct __libcpp_is_signed_impl<_Tp, false> : public true_type @@ -1226,9 +1220,7 @@ __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_signed_v = is_signed<_Tp>:: // is_unsigned template ::value> -struct __libcpp_is_unsigned_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(0) < _Tp(-1)) -{ -}; +struct __libcpp_is_unsigned_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(0) < _Tp(-1)){}; template struct __libcpp_is_unsigned_impl<_Tp, false> : public false_type @@ -2282,9 +2274,7 @@ struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, void> // bullet 1 - sizeof...(Tp) == 0 template -struct __WI_LIBCPP_TEMPLATE_VIS common_type -{ -}; +struct __WI_LIBCPP_TEMPLATE_VIS common_type{}; // bullet 2 - sizeof...(Tp) == 1 @@ -4693,7 +4683,7 @@ namespace __detail { static const bool value = #ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT - noexcept(swap_wil(declval<_Tp>(), declval<_Up>()))&& noexcept(swap_wil(declval<_Up>(), declval<_Tp>())); + noexcept(swap_wil(declval<_Tp>(), declval<_Up>())) && noexcept(swap_wil(declval<_Up>(), declval<_Tp>())); #else false; #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee8a87f8b..e4e737cb9 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/ComTests.cpp b/tests/ComTests.cpp index 0ede5ff9b..1336a1005 100644 --- a/tests/ComTests.cpp +++ b/tests/ComTests.cpp @@ -638,8 +638,9 @@ interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20a05")) IAlways : pub STDMETHOD_(void, Always)() = 0; }; -class __declspec(empty_bases) __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00")) // non-implemented to allow QI for the class to be attempted (and fail) - ComObject +class __declspec(empty_bases) +__declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00")) // non-implemented to allow QI for the class to be attempted (and fail) +ComObject : witest::AllocatedObject, public Microsoft::WRL::RuntimeClass, Microsoft::WRL::ChainInterfaces, IAlways> { @@ -655,8 +656,9 @@ class __declspec(empty_bases) __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20 } }; -class __declspec(empty_bases) __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b01")) // non-implemented to allow QI for the class to be attempted (and fail) - WinRtObject +class __declspec(empty_bases) +__declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b01")) // non-implemented to allow QI for the class to be attempted (and fail) +WinRtObject : witest::AllocatedObject, public Microsoft::WRL:: RuntimeClass, ITest, IDerivedTest, ITestInspectable, IDerivedTestInspectable, IAlways, Microsoft::WRL::FtmBase> @@ -3067,12 +3069,14 @@ TEST_CASE("COMEnumerator", "[com][enumerator]") #include #include +static unsigned long long testStartTime{}; + TEST_CASE("com_timeout", "[com][com_timeout]") { auto init = wil::CoInitializeEx_failfast(); // These test cases require calling a COM server via proxy, so that we can exercise cancellation through - // the COM runtime.. Additionally, this server needs to support the ability to control if it hangs + // the COM runtime. Additionally, this server needs to support the ability to control if it hangs // or returns quickly. The following class provides that functionality, using some global events to // provide deterministic ordering of operations. // @@ -3097,11 +3101,23 @@ TEST_CASE("com_timeout", "[com][com_timeout]") // the cancel did not work. HANDLE handles[1] = {_hangHandle.get()}; DWORD index; - REQUIRE_SUCCEEDED(CoWaitForMultipleObjects( - CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES, 10000, ARRAYSIZE(handles), handles, &index)); - + wprintf( + L"[%llu] ... ToString(): starting CoWaitForMultipleObjects\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); + const auto hr = CoWaitForMultipleObjects( + CWMO_DISPATCH_CALLS | CWMO_DISPATCH_WINDOW_MESSAGES, 15000, ARRAYSIZE(handles), handles, &index); + wprintf( + L"[%llu] ... ToString(): CoWaitForMultipleObjects return 0x%x\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime, + hr); + REQUIRE_SUCCEEDED(hr); + // if this fails with -2147417835 (0x80010115), that's the error RPC_S_CALLPENDING + // i.e., the CoWaitForMultipleObjects call timed out before the handle was signaled if (_doneHangingHandle) { + wprintf( + L"[%llu] ... To: _doneHangingHandle.SetEvent()\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); _doneHangingHandle.SetEvent(); } } @@ -3113,18 +3129,18 @@ TEST_CASE("com_timeout", "[com][com_timeout]") // The COM server thread needs an event that is signaled when we want it to stop pumping messages and // exit. wil::shared_event comServerEvent; - comServerEvent.create(); + comServerEvent.create(wil::EventOptions::ManualReset); wil::shared_event agileReferencePopulated; - agileReferencePopulated.create(); + agileReferencePopulated.create(wil::EventOptions::ManualReset); // These handles are used to coordinate with the COM server thread. The first one causes it to block. The - // done hanging event lets us know that it is done blocking and we can proceed with a second call that should + // done hanging event lets us know that it is done blocking, and we can proceed with a second call that should // avoid reentering. wil::shared_event hangingHandle; - hangingHandle.create(); + hangingHandle.create(wil::EventOptions::ManualReset); wil::shared_event doneHangingHandle; - doneHangingHandle.create(); + doneHangingHandle.create(wil::EventOptions::ManualReset); auto shouldHang = std::make_shared(false); @@ -3176,38 +3192,70 @@ TEST_CASE("com_timeout", "[com][com_timeout]") } SECTION("RPC timeout test") { - wil::com_timeout timeout{100}; - *(shouldHang.get()) = true; + hangingHandle.ResetEvent(); + doneHangingHandle.ResetEvent(); + + testStartTime = wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())); + wil::com_timeout timeout{2000}; + REQUIRE(static_cast(timeout)); - // The timeout is now in place. The blocking call should cancel in a timely manner and fail with RPC_E_CALL_CANCELED. wil::com_ptr localServer = agileStringable.query(); + + // The timeout is now in place. The blocking call should cancel in a timely manner and fail with RPC_E_CALL_CANCELED. wil::unique_hstring value; + wprintf( + L"[%llu] calling localServer->ToString\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); auto localServerResult = localServer->ToString(&value); + wprintf( + L"[%llu] localServer->ToString returned 0x%x\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime, + localServerResult); REQUIRE(static_cast(localServerResult == RPC_E_CALL_CANCELED)); REQUIRE(timeout.timed_out()); hangingHandle.SetEvent(); - REQUIRE(doneHangingHandle.wait(5000)); + wprintf( + L"[%llu] waiting for doneHangingHandle\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); + REQUIRE(doneHangingHandle.wait(10000)); + wprintf( + L"[%llu] finished waiting for doneHangingHandle\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); hangingHandle.ResetEvent(); + doneHangingHandle.ResetEvent(); - // Make a second blocking call within the lifetime of the same com_timeout instance. This second call should also - // cancel and return. + // Make a second blocking call within the lifetime of the same com_timeout instance. + // This second call should also cancel and return. + wprintf( + L"[%llu] calling localServer->ToString\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); localServerResult = localServer->ToString(&value); + wprintf( + L"[%llu] localServer->ToString returned 0x%x\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime, + localServerResult); REQUIRE(static_cast(localServerResult == RPC_E_CALL_CANCELED)); REQUIRE(timeout.timed_out()); - hangingHandle.SetEvent(); - REQUIRE(doneHangingHandle.wait(5000)); - - *(shouldHang.get()) = false; + wprintf( + L"[%llu] waiting for doneHangingHandle\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); + REQUIRE(doneHangingHandle.wait(10000)); + wprintf( + L"[%llu] finished waiting for doneHangingHandle\n", + wil::filetime::convert_100ns_to_msec(wil::filetime::to_int64(wil::filetime::get_system_time())) - testStartTime); } SECTION("Non-timeout unaffected test") { - wil::com_timeout timeout{100}; + *(shouldHang.get()) = false; + hangingHandle.ResetEvent(); + doneHangingHandle.ResetEvent(); + wil::com_timeout timeout{100}; // g_hangHandle is not set so this call will not block. It should not be affected by the timeout. wil::com_ptr localServer = agileStringable.query(); diff --git a/tests/CppWinRTAuthoringTests.cpp b/tests/CppWinRTAuthoringTests.cpp index 6053587e4..56b8a294f 100644 --- a/tests/CppWinRTAuthoringTests.cpp +++ b/tests/CppWinRTAuthoringTests.cpp @@ -19,8 +19,8 @@ struct my_async_status : winrt::implements ErrorCode; wil::single_threaded_property Id{16}; - void Cancel(){}; - void Close(){}; + void Cancel() {}; + void Close() {}; }; // This type has a settable property diff --git a/tests/NetworkingTests.cpp b/tests/NetworkingTests.cpp new file mode 100644 index 000000000..481c32c34 --- /dev/null +++ b/tests/NetworkingTests.cpp @@ -0,0 +1,2461 @@ +#include "pch.h" + +#include +#include +#include +#include +#include + +// set this to give access to all functions +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#define WI_NETWORK_TEST +#include + +#include "common.h" + +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; + +static INIT_ONCE SocketTestInit = INIT_ONCE_STATIC_INIT; + +static BOOL WINAPI InitializeAllStrings(PINIT_ONCE, PVOID, PVOID*) +{ + 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; + } + + 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; + } + + 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; +} +static void InitTestAddresses() +{ + InitOnceExecuteOnce(&SocketTestInit, InitializeAllStrings, nullptr, nullptr); +} + +TEST_CASE("NetworkingTests::Verifying_wsastartup_cleanup", "[networking]") +{ + // verify socket APIs fail without having called WSAStartup + // 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") + { + const auto cleanup = wil::network::WSAStartup_nothrow(); + const bool succeeded = !!cleanup; + 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::network::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::network::WSAStartup(); + const auto socket_test = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + REQUIRE(socket_test != INVALID_SOCKET); + ::closesocket(socket_test); + } +#endif +} + +TEST_CASE("NetworkingTests::Verifying_constructors", "[networking]") +{ + using wil::network::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 + + 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); + 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 + + 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)") + { + 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_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 + 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 + + 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*)") + { + 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 + + 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*)") + { + 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 + + 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*)") + { + 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_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 + 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))); + + 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*)") + { + 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(); + + 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}; + wil::network::socket_address test_v4_addr_with_port{&Test_in_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::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::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::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::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::network::equals; + + SECTION("IPv4 in_addr properties") + { + REQUIRE(test_v4_addr.family() == AF_INET); + 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.address_type()); + REQUIRE(NlatUnicast == test_v4_addr2.address_type()); + + 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); + + 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") + { + REQUIRE(test_v4_addr_with_port.family() == AF_INET); + 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.address_type()); + + 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); + + 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") + { + REQUIRE(test_v6_addr.family() == AF_INET6); + 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_addr2.address_type()); + + 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); + + 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") + { + REQUIRE(test_v6_addr_with_port.family() == AF_INET6); + 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.address_type()); + + 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); + + 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); + 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); + REQUIRE(default_addr < test_v6_addr_with_port); + } + + SECTION("IPv4 link-local in_addr properties") + { + REQUIRE(test_v4_linklocal_addr.family() == AF_INET); + 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(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); + + 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); + 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.address_type() == NlatUnicast); + REQUIRE(test_v4_linklocal_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v4_linklocal_addr_with_port.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnicast); + REQUIRE(test_v6_linklocal_addr.is_address_linklocal()); + REQUIRE(!test_v6_linklocal_addr.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnicast); + REQUIRE(test_v6_linklocal_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v6_linklocal_addr_with_port.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnspecified); + REQUIRE(!test_v4_any_addr.is_address_linklocal()); + REQUIRE(!test_v4_any_addr.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnspecified); + REQUIRE(!test_v4_any_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v4_any_addr_with_port.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnspecified); + REQUIRE(!test_v6_any_addr.is_address_linklocal()); + REQUIRE(!test_v6_any_addr.is_address_loopback()); + + 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); + + 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); + 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.address_type() == NlatUnspecified); + REQUIRE(!test_v6_any_addr_with_port.is_address_linklocal()); + REQUIRE(!test_v6_any_addr_with_port.is_address_loopback()); + + 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); + + 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); + 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); + } +} + +TEST_CASE("NetworkingTests::Verifying_operators", "[networking]") +{ + using wil::network::equals; + +#ifdef WIL_ENABLE_EXCEPTIONS + using wil::network::socket_address; + SECTION("verify v4 address comparisons") + { + 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") + { + 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); + 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); + REQUIRE(!(lhs_flow_info_test == rhs_flow_info_test)); + } +#endif +} + +TEST_CASE("NetworkingTests::Verifying_set_functions", "[networking]") +{ + InitTestAddresses(); + + 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}; + wil::network::socket_address test_v4_addr_with_port{&Test_in_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::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::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::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::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::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::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::network::WSAStartup_nothrow(); + REQUIRE(static_cast(wsa_startup_tracking)); + + using wil::network::equals; + + SECTION("verify set_address_any") + { + 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 + 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::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))); + + 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::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 + 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::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))); + + 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::network::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.address_type() == NlatUnspecified); + REQUIRE(!v4_address.is_address_linklocal()); + REQUIRE(!v4_address.is_address_loopback()); + 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.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.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::network::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.address_type() == NlatUnspecified); + REQUIRE(!v6_address.is_address_linklocal()); + REQUIRE(!v6_address.is_address_loopback()); + 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.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.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::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); + 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); + 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.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::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); + 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); + 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.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::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 + 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::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 + 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::network::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::network::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::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); + 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::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); + 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::network::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::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))); + + 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::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))); + + 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::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(), test_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::network::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::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(), test_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::network::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::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))); + + 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::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))); + + 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::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(), test_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::network::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::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(), test_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::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()); + 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::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()); + 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::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()); + 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::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()); + 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::network::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::network::socket_address v4_addr{&v4_test_sockaddr}; + + 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 + 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::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)}; + REQUIRE(test_socket.get() != INVALID_SOCKET); + + wil::network::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::network::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 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('\0' == string_address[0]); + REQUIRE(SUCCEEDED(af_unspec_address.write_complete_address_nothrow(string_address))); + REQUIRE('\0' == string_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()); + + test_string = af_unspec_address.write_complete_address(); + REQUIRE(test_string.empty()); +#endif + } + + SECTION("verify write_address_nothrow failure path") + { + wil::network::socket_address test_address; + // set an unsupported family for verifying failure paths + test_address.reset(AF_APPLETALK); + + 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))); + +#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::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))); + } + + SECTION("verify write_address_nothrow maximum string size") + { + { + 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::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 + 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::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::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 + 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::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::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); + 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::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(), listenAddress.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 = 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); + + 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::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(), connecting_from_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(), listenAddress.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::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); + 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::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(); + InitTestAddresses(); + + SECTION("verify resolve_local_addresses") + { +#if (defined(WIL_ENABLE_EXCEPTIONS)) + { + 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()); + + 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; + } + + REQUIRE(count > 0); + } +#endif + } + + SECTION("verify resolve_localhost_addresses") + { +#if (defined(WIL_ENABLE_EXCEPTIONS)) + const wil::unique_addrinfo test_addr = wil::network::resolve_localhost_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()); + + uint32_t count = 0; + 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)); + 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::network::socket_address_wstring address_string; + REQUIRE(SUCCEEDED(address.write_address_nothrow(address_string))); + wprintf(L"... resolve_localhost_addresses : %ws\n", address_string); + ++count; + } + + REQUIRE(count > 0); +#endif + } + + SECTION("verify const addr_info_iterator") + { + 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()); + } + + SECTION("verify addr_info_iterator increment") + { + 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 = begin(addr_info_iterator{initial_addr.get()}); it != end(addr_info_iterator{}); ++it) + { + ++total_count; + } + + 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 == end(addr_info_iterator{})); + } + + SECTION("verify addr_info_iterator move behavior") + { + 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()} == 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()}, end(addr_info_iterator{}))) + { + 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 resolve_local_addresses : %ws\n", address_string); + } + } + + SECTION("verify addr_info_iterator move assignment behavior") + { + 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{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()} == 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()} != end(addr_info_iterator{})); + + for (const auto& address : addr_info_iterator{moved_to_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"... move assignment resolve_local_addresses : %ws\n", address_string); + } + } + + // 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()} != end(addr_info_ansi_iterator{})); + + auto total_count = 0; + 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()} != 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 == 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()} != 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()} == 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(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)); + 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()} != 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()} == 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()} != end(addr_info_ansi_iterator{})); + + for (const auto& address : addr_info_ansi_iterator{moved_to_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"... 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(begin(addr_infoex_iterator{initial_addr.get()}) != end(addr_infoex_iterator{})); + + auto total_count = 0; + 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()} != wil::network::end(addr_infoex_iterator{})); + + addr_infoex_iterator test_iterator = addr_infoex_iterator{test_addr.get()}; + test_iterator += total_count; + 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()} != 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()} == end(addr_infoex_iterator{})); + REQUIRE(addr_infoex_iterator{moved_to_addr.get()} != end(addr_infoex_iterator{})); + + for (const auto& address : addr_infoex_iterator{moved_to_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"... 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()} != 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()} == 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()} != end(addr_infoex_iterator{})); + + 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)); + 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 diff --git a/tests/ResourceTests.cpp b/tests/ResourceTests.cpp index 19ca2b1a3..6658265d6 100644 --- a/tests/ResourceTests.cpp +++ b/tests/ResourceTests.cpp @@ -147,7 +147,7 @@ class __declspec(empty_bases) PointerTestObject public Microsoft::WRL::RuntimeClass, ITest> { public: - STDMETHOD_(void, Test)(){}; + STDMETHOD_(void, Test)() {}; }; TEST_CASE("ResourceTests::TestOperationsOnGenericSmartPointerClasses", "[resource]") diff --git a/tests/cpplatest/CMakeLists.txt b/tests/cpplatest/CMakeLists.txt index 060881691..e16f661b5 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 diff --git a/tests/noexcept/CMakeLists.txt b/tests/noexcept/CMakeLists.txt index 59cd2dad4..f7f81224f 100644 --- a/tests/noexcept/CMakeLists.txt +++ b/tests/noexcept/CMakeLists.txt @@ -24,6 +24,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 7b651c2f4..6164e3cfc 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/wiTest.cpp b/tests/wiTest.cpp index 579b5f9c0..cee92bea1 100644 --- a/tests/wiTest.cpp +++ b/tests/wiTest.cpp @@ -2136,9 +2136,11 @@ TEST_CASE("WindowsInternalTests::WistdTests", "[resource][wistd]") spConstruct.reset(); spConstruct.release(); +#if !defined(WITEST_ADDRESS_SANITIZER) auto spTooBig = wil::make_unique_nothrow(static_cast(-1)); REQUIRE_FALSE(spTooBig); // REQUIRE_FAILFAST_UNSPECIFIED([]{ auto spTooBigFF = wil::make_unique_failfast(static_cast(-1)); }); +#endif object_counter_state state; count = 0; diff --git a/tests/win7/CMakeLists.txt b/tests/win7/CMakeLists.txt index 15445f9f7..736cec8b5 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