From a45cb9690d6298067a8cf9e5d69b9f006833034d Mon Sep 17 00:00:00 2001 From: Keith Marshall Date: Mon, 20 Jan 2020 18:03:57 +0000 Subject: [PATCH] Implement sockets fall-back API. --- w32api/ChangeLog | 20 + w32api/include/ws2tcpip.h | 734 ++++++++++++++++++++-------------- w32api/include/wspiapi.h | 980 ++++++++++++++++++++++++++++++++++++++++++++++ w32api/tests/headers.at | 1 + 4 files changed, 1436 insertions(+), 299 deletions(-) create mode 100644 w32api/include/wspiapi.h diff --git a/w32api/ChangeLog b/w32api/ChangeLog index db87969..378e663 100644 --- a/w32api/ChangeLog +++ b/w32api/ChangeLog @@ -1,3 +1,23 @@ +2020-01-20 Keith Marshall + + Implement sockets fall-back API. + + * tests/headers.at: Add... + * include/wspiapi.h: ...this new file; it implements... + (WspiapiGetAddrInfo, WspiapiGetNameInfo, WspiapiFreeAddrInfo): + ...these inline fall-back replacement implementations for... + (getaddrinfo, getnameinfo, freeaddrinfo): ...these IETF RFC 3493 + functions, respectively, per hints in Microsoft online docs. + + * include/ws2tcpip.h: Tidy layout; assert copyright. + (pragma GCC system_header): Remove redundant GCC version guard. + (EAI_SYSTEM, EAI_OVERFLOW): New symbolic error codes; define them. + (socklen_t): Correct typedef; was signed but negative is meaningless. + (getnameinfo): Adjust prototype declaration to conform to RFC 3493. + (__AW_SUFFIXED__): Use it to selectively map definitions for... + [UNICODE vs. ! UNICODE] (gai_strerror): ...this function. + (_BEGIN_C_DECLS, _END_C_DECLS): Use them. + 2020-01-17 Keith Marshall Preserve order of tests for integrity of header files. diff --git a/w32api/include/ws2tcpip.h b/w32api/include/ws2tcpip.h index 1e8e790..da9a306 100644 --- a/w32api/include/ws2tcpip.h +++ b/w32api/include/ws2tcpip.h @@ -1,379 +1,515 @@ /* - * ws2tcpip.h : TCP/IP specific extensions in Windows Sockets 2 + * ws2tcpip.h + * + * TCP/IP specific extensions to the Windows Sockets API v2 + * + * + * $Id$ + * + * Contributed by Danny Smith + * Copyright (C) 2001-2003, 2005-2008, 2020, MinGW.org Project * * Portions Copyright (c) 1980, 1983, 1988, 1993 * The Regents of the University of California. All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * */ - #ifndef _WS2TCPIP_H -#define _WS2TCPIP_H -#if __GNUC__ >=3 #pragma GCC system_header -#endif +#define _WS2TCPIP_H -#if (defined _WINSOCK_H && !defined _WINSOCK2_H) -#error "ws2tcpip.h is not compatible with winsock.h. Include winsock2.h instead." +#if defined _WINSOCK_H && ! defined _WINSOCK2_H +#error "ws2tcpip.h is not compatible with winsock.h; include winsock2.h instead." #endif #include -#ifdef __cplusplus -extern "C" { -#endif -/* - * The IP_* macros are also defined in winsock.h, but some values are different there. - * The values defined in winsock.h for 1.1 and used in wsock32.dll are consistent - * with the original values Steve Deering defined in his document "IP Multicast Extensions - * for 4.3BSD UNIX related systems (MULTICAST 1.2 Release)." However, these conflicted with - * the definitions for some IPPROTO_IP level socket options already assigned by BSD, - * so Berkeley changed all the values by adding 7. WinSock2 (ws2_32.dll) uses +_BEGIN_C_DECLS + +/* The IP_* macros are also defined in winsock.h, but some values are different there. + * The values defined in winsock.h for 1.1 and used in wsock32.dll are consistent with + * the original values Steve Deering defined in his document, "IP Multicast Extensions + * for 4.3BSD UNIX related systems (MULTICAST 1.2 Release)". However, these conflict + * with the definitions for some IPPROTO_IP level socket options already assigned by + * BSD, so Berkeley changed all the values by adding 7. WinSock2 (ws2_32.dll) uses * the BSD 4.4 compatible values defined here. * * See also: msdn kb article Q257460 * http://support.microsoft.com/support/kb/articles/Q257/4/60.asp */ - -/* This is also defined in winsock.h; value hasn't changed */ -#define IP_OPTIONS 1 - -#define IP_HDRINCL 2 -/* - * These are also be defined in winsock.h, - * but values have changed for WinSock2 interface - */ -#define IP_TOS 3 /* old (winsock 1.1) value 8 */ -#define IP_TTL 4 /* old value 7 */ -#define IP_MULTICAST_IF 9 /* old value 2 */ -#define IP_MULTICAST_TTL 10 /* old value 3 */ -#define IP_MULTICAST_LOOP 11 /* old value 4 */ -#define IP_ADD_MEMBERSHIP 12 /* old value 5 */ -#define IP_DROP_MEMBERSHIP 13 /* old value 6 */ -#define IP_DONTFRAGMENT 14 /* old value 9 */ -#define IP_ADD_SOURCE_MEMBERSHIP 15 -#define IP_DROP_SOURCE_MEMBERSHIP 16 -#define IP_BLOCK_SOURCE 17 -#define IP_UNBLOCK_SOURCE 18 -#define IP_PKTINFO 19 - -/* - * As with BSD implementation, IPPROTO_IPV6 level socket options have - * same values as IPv4 counterparts. +#define IP_OPTIONS 1 /* unchanged from WinSock v1.1 */ +#define IP_HDRINCL 2 /* unchanged from WinSock v1.1 */ +#define IP_TOS 3 /* had value 8 in WinSock v1.1 */ +#define IP_TTL 4 /* had value 7 in WinSock v1.1 */ +#define IP_MULTICAST_IF 9 /* had value 2 in WinSock v1.1 */ +#define IP_MULTICAST_TTL 10 /* had value 3 in WinSock v1.1 */ +#define IP_MULTICAST_LOOP 11 /* had value 4 in WinSock v1.1 */ +#define IP_ADD_MEMBERSHIP 12 /* had value 5 in WinSock v1.1 */ +#define IP_DROP_MEMBERSHIP 13 /* had value 6 in WinSock v1.1 */ +#define IP_DONTFRAGMENT 14 /* had value 9 in WinSock v1.1 */ +#define IP_ADD_SOURCE_MEMBERSHIP 15 /* undefined in WinSock v1.1 */ +#define IP_DROP_SOURCE_MEMBERSHIP 16 /* undefined in WinSock v1.1 */ +#define IP_BLOCK_SOURCE 17 /* undefined in WinSock v1.1 */ +#define IP_UNBLOCK_SOURCE 18 /* undefined in WinSock v1.1 */ +#define IP_PKTINFO 19 /* undefined in WinSock v1.1 */ + +/* As with BSD implementation, IPPROTO_IPV6 level socket options have + * the same values as their IPv4 counterparts. */ -#define IPV6_UNICAST_HOPS 4 -#define IPV6_MULTICAST_IF 9 -#define IPV6_MULTICAST_HOPS 10 -#define IPV6_MULTICAST_LOOP 11 -#define IPV6_ADD_MEMBERSHIP 12 -#define IPV6_DROP_MEMBERSHIP 13 -#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP -#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP -#define IPV6_PKTINFO 19 +#define IPV6_UNICAST_HOPS 4 +#define IPV6_MULTICAST_IF 9 +#define IPV6_MULTICAST_HOPS 10 +#define IPV6_MULTICAST_LOOP 11 +#define IPV6_ADD_MEMBERSHIP 12 +#define IPV6_DROP_MEMBERSHIP 13 +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#define IPV6_PKTINFO 19 -#define IP_DEFAULT_MULTICAST_TTL 1 -#define IP_DEFAULT_MULTICAST_LOOP 1 -#define IP_MAX_MEMBERSHIPS 20 +#define IP_DEFAULT_MULTICAST_TTL 1 +#define IP_DEFAULT_MULTICAST_LOOP 1 +#define IP_MAX_MEMBERSHIPS 20 -#define TCP_EXPEDITED_1122 2 +#define TCP_EXPEDITED_1122 2 -#define UDP_NOCHECKSUM 1 +#define UDP_NOCHECKSUM 1 -/* INTERFACE_INFO iiFlags */ -#define IFF_UP 1 -#define IFF_BROADCAST 2 -#define IFF_LOOPBACK 4 -#define IFF_POINTTOPOINT 8 -#define IFF_MULTICAST 16 +/* INTERFACE_INFO iiFlags + */ +#define IFF_UP 1 +#define IFF_BROADCAST 2 +#define IFF_LOOPBACK 4 +#define IFF_POINTTOPOINT 8 +#define IFF_MULTICAST 16 -#define SIO_GET_INTERFACE_LIST _IOR('t', 127, u_long) +#define SIO_GET_INTERFACE_LIST _IOR('t', 127, u_long) -#define INET_ADDRSTRLEN 16 -#define INET6_ADDRSTRLEN 46 +/* Minimum buffer sizes required, to store any arbitrary IPv4, + * or IPv6 address, respectively, in string format. + */ +#define INET_ADDRSTRLEN 16 +#define INET6_ADDRSTRLEN 46 -/* getnameinfo constants */ -#define NI_MAXHOST 1025 -#define NI_MAXSERV 32 +/* Constants for use when calling the getnameinfo() function; + * first, the recommended lengths for host-name and service-name + * return data buffers... + */ +#define NI_MAXHOST 1025 +#define NI_MAXSERV 32 +/* ...and also, symbolic names for the flags argument bits. + */ #define NI_NOFQDN 0x01 #define NI_NUMERICHOST 0x02 #define NI_NAMEREQD 0x04 #define NI_NUMERICSERV 0x08 #define NI_DGRAM 0x10 -/* getaddrinfo constants */ -#define AI_PASSIVE 1 -#define AI_CANONNAME 2 -#define AI_NUMERICHOST 4 - -/* getaddrinfo error codes */ -#define EAI_AGAIN WSATRY_AGAIN -#define EAI_BADFLAGS WSAEINVAL -#define EAI_FAIL WSANO_RECOVERY -#define EAI_FAMILY WSAEAFNOSUPPORT -#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY -#define EAI_NODATA WSANO_DATA -#define EAI_NONAME WSAHOST_NOT_FOUND -#define EAI_SERVICE WSATYPE_NOT_FOUND -#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT - -/* - * ip_mreq also in winsock.h for WinSock1.1, - * but online msdn docs say it is defined here for WinSock2. +/* Options which may be passed, as inclusive-or bit flags, in + * the hints->ai_flags argument, to getaddrinfo() */ - -struct ip_mreq { - struct in_addr imr_multiaddr; - struct in_addr imr_interface; +#define AI_PASSIVE 1 +#define AI_CANONNAME 2 +#define AI_NUMERICHOST 4 + +/* Error codes which may be returned by the getaddrinfo() and + * getnameinfo() functions. These are the symbolic names which + * conform to IETF convention; each represents, and is mapped + * to, a corresponding WinSock error condition. + */ +#define EAI_AGAIN WSATRY_AGAIN +#define EAI_BADFLAGS WSAEINVAL +#define EAI_FAIL WSANO_RECOVERY +#define EAI_FAMILY WSAEAFNOSUPPORT +#define EAI_MEMORY WSA_NOT_ENOUGH_MEMORY +#define EAI_NODATA WSANO_DATA +#define EAI_NONAME WSAHOST_NOT_FOUND +#define EAI_OVERFLOW WSAENAMETOOLONG +#define EAI_SERVICE WSATYPE_NOT_FOUND +#define EAI_SOCKTYPE WSAESOCKTNOSUPPORT +#define EAI_SYSTEM WSASYSCALLFAILURE + +/* The ip_mreq structure is also required by WinSock v1.1, for + * which case it is declared in , since the definition + * here is visible only to WinSock v2 applications which include + * this header file. + */ +struct ip_mreq +{ struct in_addr imr_multiaddr; + struct in_addr imr_interface; }; -struct ip_mreq_source { - struct in_addr imr_multiaddr; - struct in_addr imr_sourceaddr; - struct in_addr imr_interface; +struct ip_mreq_source +{ struct in_addr imr_multiaddr; + struct in_addr imr_sourceaddr; + struct in_addr imr_interface; }; -struct ip_msfilter { - struct in_addr imsf_multiaddr; - struct in_addr imsf_interface; - u_long imsf_fmode; - u_long imsf_numsrc; - struct in_addr imsf_slist[1]; +struct ip_msfilter +{ struct in_addr imsf_multiaddr; + struct in_addr imsf_interface; + u_long imsf_fmode; + u_long imsf_numsrc; + struct in_addr imsf_slist[1]; }; -#define IP_MSFILTER_SIZE(numsrc) \ - (sizeof(struct ip_msfilter) - sizeof(struct in_addr) \ - + (numsrc) * sizeof(struct in_addr)) - -struct in_pktinfo { - IN_ADDR ipi_addr; - UINT ipi_ifindex; -}; -typedef struct in_pktinfo IN_PKTINFO; +#define IP_MSFILTER_SIZE(numsrc) \ + ( sizeof( struct ip_msfilter ) - sizeof( struct in_addr ) \ + + (numsrc) * sizeof( struct in_addr ) \ + ) +typedef +struct in_pktinfo +{ IN_ADDR ipi_addr; + UINT ipi_ifindex; +} IN_PKTINFO; -/* ipv6 */ -/* These require XP or .NET Server or use of add-on IPv6 stacks on NT 4 - or higher */ - -/* This is based on the example given in RFC 2553 with stdint types - changed to BSD types. For now, use these field names until there - is some consistency in MS docs. In this file, we only use the - in6_addr structure start address, with casts to get the right offsets - when testing addresses */ - -struct in6_addr { - union { - u_char _S6_u8[16]; - u_short _S6_u16[8]; - u_long _S6_u32[4]; - } _S6_un; +/* IPv6 + * + * This implementation requires WinXP or .NET Server or use of add-on + * IPv6 stacks on WinNT4 and Win2K legacy platforms. + * + * The following in6_addr definition is based on the example given in + * RFC 3493, with stdint types replaced by BSD types. For now, we use + * these field names until there is some consistency in MS docs; Within + * this file, we only use the in6_addr structure start address, with + * casts to get the right offsets when testing addresses. + */ +struct in6_addr +{ union + { u_char _S6_u8[16]; + u_short _S6_u16[8]; + u_long _S6_u32[4]; + } _S6_un; +# define s6_addr _S6_un._S6_u8 /* RFC 3493 standard name */ }; -/* s6_addr is the standard name */ -#define s6_addr _S6_un._S6_u8 -/* These are GLIBC names */ +/* The following are GLIBC specific field names... + */ #define s6_addr16 _S6_un._S6_u16 #define s6_addr32 _S6_un._S6_u32 -/* These are used in some MS code */ -#define in_addr6 in6_addr +/* ...while these may be found in some Microsoft code. + */ +#define in_addr6 in6_addr #define _s6_bytes _S6_un._S6_u8 #define _s6_words _S6_un._S6_u16 typedef struct in6_addr IN6_ADDR, *PIN6_ADDR, *LPIN6_ADDR; -struct sockaddr_in6 { - short sin6_family; /* AF_INET6 */ - u_short sin6_port; /* transport layer port # */ - u_long sin6_flowinfo; /* IPv6 traffic class & flow info */ - struct in6_addr sin6_addr; /* IPv6 address */ - u_long sin6_scope_id; /* set of interfaces for a scope */ -}; -typedef struct sockaddr_in6 SOCKADDR_IN6, *PSOCKADDR_IN6, *LPSOCKADDR_IN6; +typedef +struct sockaddr_in6 +{ short sin6_family; /* AF_INET6 */ + u_short sin6_port; /* transport layer port # */ + u_long sin6_flowinfo; /* IPv6 traffic class & flow info */ + struct in6_addr sin6_addr; /* IPv6 address */ + u_long sin6_scope_id; /* set of interfaces for a scope */ +} SOCKADDR_IN6, *PSOCKADDR_IN6, *LPSOCKADDR_IN6; extern const struct in6_addr in6addr_any; extern const struct in6_addr in6addr_loopback; -/* the above can get initialised using: */ + #define IN6ADDR_ANY_INIT { 0 } #define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } -/* Described in RFC 2292, but not in 2553 */ -/* int IN6_ARE_ADDR_EQUAL(const struct in6_addr * a, const struct in6_addr * b) */ -#define IN6_ARE_ADDR_EQUAL(a, b) \ - (memcmp ((void*)(a), (void*)(b), sizeof (struct in6_addr)) == 0) - -/* Address Testing Macros - - These macro functions all take const struct in6_addr* as arg. - Static inlines would allow type checking, but RFC 2553 says they - macros. - NB: These are written specifically for little endian host */ - -#define IN6_IS_ADDR_UNSPECIFIED(_addr) \ - ( (((const u_long *)(_addr))[0] == 0) \ - && (((const u_long *)(_addr))[1] == 0) \ - && (((const u_long *)(_addr))[2] == 0) \ - && (((const u_long *)(_addr))[3] == 0)) - -#define IN6_IS_ADDR_LOOPBACK(_addr) \ - ( (((const u_long *)(_addr))[0] == 0) \ - && (((const u_long *)(_addr))[1] == 0) \ - && (((const u_long *)(_addr))[2] == 0) \ - && (((const u_long *)(_addr))[3] == 0x01000000)) /* Note byte order reversed */ -/* (((const u_long *)(_addr))[3] == ntohl(1)) */ - -#define IN6_IS_ADDR_MULTICAST(_addr) (((const u_char *) (_addr))[0] == 0xff) - -#define IN6_IS_ADDR_LINKLOCAL(_addr) \ - ( (((const u_char *)(_addr))[0] == 0xfe) \ - && ((((const u_char *)(_addr))[1] & 0xc0) == 0x80)) - -#define IN6_IS_ADDR_SITELOCAL(_addr) \ - ( (((const u_char *)(_addr))[0] == 0xfe) \ - && ((((const u_char *)(_addr))[1] & 0xc0) == 0xc0)) - -#define IN6_IS_ADDR_V4MAPPED(_addr) \ - ( (((const u_long *)(_addr))[0] == 0) \ - && (((const u_long *)(_addr))[1] == 0) \ - && (((const u_long *)(_addr))[2] == 0xffff0000)) /* Note byte order reversed */ -/* (((const u_long *)(_addr))[2] == ntohl(0x0000ffff))) */ - -#define IN6_IS_ADDR_V4COMPAT(_addr) \ - ( (((const u_long *)(_addr))[0] == 0) \ - && (((const u_long *)(_addr))[1] == 0) \ - && (((const u_long *)(_addr))[2] == 0) \ - && (((const u_long *)(_addr))[3] != 0) \ - && (((const u_long *)(_addr))[3] != 0x01000000)) /* Note byte order reversed */ -/* (ntohl (((const u_long *)(_addr))[3]) > 1 ) */ - - -#define IN6_IS_ADDR_MC_NODELOCAL(_addr) \ - ( IN6_IS_ADDR_MULTICAST(_addr) \ - && ((((const u_char *)(_addr))[1] & 0xf) == 0x1)) - -#define IN6_IS_ADDR_MC_LINKLOCAL(_addr) \ - ( IN6_IS_ADDR_MULTICAST (_addr) \ - && ((((const u_char *)(_addr))[1] & 0xf) == 0x2)) - -#define IN6_IS_ADDR_MC_SITELOCAL(_addr) \ - ( IN6_IS_ADDR_MULTICAST(_addr) \ - && ((((const u_char *)(_addr))[1] & 0xf) == 0x5)) - -#define IN6_IS_ADDR_MC_ORGLOCAL(_addr) \ - ( IN6_IS_ADDR_MULTICAST(_addr) \ - && ((((const u_char *)(_addr))[1] & 0xf) == 0x8)) - -#define IN6_IS_ADDR_MC_GLOBAL(_addr) \ - ( IN6_IS_ADDR_MULTICAST(_addr) \ - && ((((const u_char *)(_addr))[1] & 0xf) == 0xe)) - - -typedef int socklen_t; - -struct ipv6_mreq { - struct in6_addr ipv6mr_multiaddr; - unsigned int ipv6mr_interface; +/* IPv6 Address Testing Macros + * + * Each of these macros, as specified in RFC 3493, exhibits semantics + * of a function taking a "const struct in6_addr *" argument; (static + * inline functions would allow type checking, but RFC 3493 stipulates + * that they are macros, so we implement them as such). + * + * Note that IPv6 addresses are expressed in big-endian byte order. + * Host-native addressing is little-endian; thus, we implement each of + * these macros to interpret its argument as a byte array, stored in + * big-endian order, (reversing byte order throughout the quad-byte + * representation of the array). + * + * Further note that, while each of the following macros is named as + * specified in RFC 3493, the bit patterns, on which classification of + * the addresses is based, are specified in RFC 4291. + */ +#define IN6_IS_ADDR_UNSPECIFIED(_addr) \ +/* All 16 bytes of the IPv6 unspecified (i.e. the wildcard) \ + * address MUST be zero. \ + */ \ + ( (((const u_long *)(_addr))[0] == 0) \ + && (((const u_long *)(_addr))[1] == 0) \ + && (((const u_long *)(_addr))[2] == 0) \ + && (((const u_long *)(_addr))[3] == 0) \ + ) + +#define IN6_IS_ADDR_LOOPBACK(_addr) \ +/* Loopback address is ::1; for big-endian storage, the least \ + * significant byte, with value one, must be moved to the most \ + * significant byte of the most significant quad-byte, giving \ + * it an effective value of 0x01000000 \ + */ \ + ( (((const u_long *)(_addr))[0] == 0) \ + && (((const u_long *)(_addr))[1] == 0) \ + && (((const u_long *)(_addr))[2] == 0) \ + && (((const u_long *)(_addr))[3] == 0x01000000) \ + ) + +#define IN6_IS_ADDR_LINKLOCAL(_addr) \ +/* IPv6 link-local addresses are identified by having their \ + * ten most significant bits equal to 1111111010b \ + */ \ + ( (((const u_char *)(_addr))[0] == 0xfe) \ + && ((((const u_char *)(_addr))[1] & 0xc0) == 0x80) \ + ) + +#define IN6_IS_ADDR_SITELOCAL(_addr) \ +/* RFC 4291 says that IPv6 site-local addresses are obsolete, \ + * and forbids their use in new applications; nonetheless they \ + * are still permitted in pre-existing applications, in which \ + * they may be indentified by having their most significant \ + * ten bits equal to 1111111011b \ + */ \ + ( (((const u_char *)(_addr))[0] == 0xfe) \ + && ((((const u_char *)(_addr))[1] & 0xc0) == 0xc0) \ + ) + +#define IN6_IS_ADDR_V4MAPPED(_addr) \ +/* An IPv4 mapped address reserves the least significant four \ + * bytes, (most significant in big-endian equivalent storage), \ + * for the IPv4 address; the immediately adjacent two bytes \ + * MUST be 0xffff, with the remaining ten bytes all zero. \ + */ \ + ( (((const u_long *)(_addr))[0] == 0) \ + && (((const u_long *)(_addr))[1] == 0) \ + && (((const u_long *)(_addr))[2] == 0xffff0000) \ + ) + +#define IN6_IS_ADDR_V4COMPAT(_addr) \ +/* IPv4 compatible address format is effectively obsolete; \ + * nonetheless, it may be recognized as being the same as the \ + * IPv4 mapped format, but with 0x0000 in place of the 0xffff \ + * byte pair, and the IPv4 address represented being neither \ + * 0.0.0.0, nor 0.0.0.1, (again noting the byte reversal for \ + * big-endian storage order). \ + */ \ + ( (((const u_long *)(_addr))[0] == 0) \ + && (((const u_long *)(_addr))[1] == 0) \ + && (((const u_long *)(_addr))[2] == 0) \ + && (((const u_long *)(_addr))[3] != 0) \ + && (((const u_long *)(_addr))[3] != 0x01000000) \ + ) + +#define IN6_IS_ADDR_MULTICAST(_addr) \ +/* Any IPv6 address in which the most significant byte, (least \ + * significant in host storage order), has a value of 0xff, is \ + * a multicast address. \ + */ \ + (((const u_char *)(_addr))[0] == 0xff) + +/* IPv6 multicast addresses may be further classified into + * "scope" groups, based on the value of the low order four + * bits of the second most significant byte; as above, the + * following macro names are specified in RFC 3493, but the + * scope indices are specified in RFC 4291. + */ +#define IN6_IS_ADDR_MC_NODELOCAL(_addr) \ +/* IPv6 multicast addresses with a scope index of one refer \ + * only to interfaces of the originating host node; (this is \ + * a multicast equivalent to the loopback address). \ + */ \ + ( IN6_IS_ADDR_MULTICAST(_addr) \ + && ((((const u_char *)(_addr))[1] & 0xf) == 0x1) \ + ) + +#define IN6_IS_ADDR_MC_LINKLOCAL(_addr) \ +/* IPv6 multicast addresses with a scope index of two refer \ + * to interfaces on the same subnet as the originating host. \ + */ \ + ( IN6_IS_ADDR_MULTICAST(_addr) \ + && ((((const u_char *)(_addr))[1] & 0xf) == 0x2) \ + ) + +#define IN6_IS_ADDR_MC_SITELOCAL(_addr) \ +/* IPv6 multicast addresses with a scope index of five refer \ + * to interfaces, possibly spanning multiple subnets, but all \ + * within the confines of a single geographical location. \ + */ \ + ( IN6_IS_ADDR_MULTICAST(_addr) \ + && ((((const u_char *)(_addr))[1] & 0xf) == 0x5) \ + ) + +#define IN6_IS_ADDR_MC_ORGLOCAL(_addr) \ +/* IPv6 multicast addresses with a scope index of eight refer \ + * to interfaces which are geographically distributed across \ + * multiple sites, all belonging to a single organization. \ + */ \ + ( IN6_IS_ADDR_MULTICAST(_addr) \ + && ((((const u_char *)(_addr))[1] & 0xf) == 0x8) \ + ) + +#define IN6_IS_ADDR_MC_GLOBAL(_addr) \ +/* IPv6 multicast addresses with a scope index of fourteen \ + * refer to interfaces which may be globally distributed over \ + * the public internet. \ + */ \ + ( IN6_IS_ADDR_MULTICAST(_addr) \ + && ((((const u_char *)(_addr))[1] & 0xf) == 0xe) \ + ) + +/* RFC 3542 augments the preceding set of address testing macros, by the + * addition of one more, intended for use in "advanced" applications; it + * is semantically equivalent to a function with prototype: + * + * int IN6_ARE_ADDR_EQUAL + * ( const struct in6_addr *, const struct in6_addr * ) + * + * and returns non-zero if its two IPv6 address arguments are the same, + * or zero, if they differ. + */ +#define IN6_ARE_ADDR_EQUAL(_addr1, _addr2) \ + ( memcmp( (void *)(_addr1), (void *)(_addr2), \ + sizeof( struct in6_addr ) \ + ) == 0 \ + ) + +typedef unsigned int socklen_t; + +typedef +struct ipv6_mreq +{ struct in6_addr ipv6mr_multiaddr; + unsigned int ipv6mr_interface; +} IPV6_MREQ; + +typedef +struct in6_pktinfo +{ IN6_ADDR ipi6_addr; + UINT ipi6_ifindex; +} IN6_PKTINFO; + +struct addrinfo +{ int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; }; -typedef struct ipv6_mreq IPV6_MREQ; -struct in6_pktinfo { - IN6_ADDR ipi6_addr; - UINT ipi6_ifindex; -}; -typedef struct in6_pktinfo IN6_PKTINFO; - -struct addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - size_t ai_addrlen; - char *ai_canonname; - struct sockaddr *ai_addr; - struct addrinfo *ai_next; -}; +#if _WIN32_WINNT >= _WIN32_WINNT_WINXP && ! defined _WSPIAPI_H +/* The following three functions are required by RFC 3493, (formerly + * RFC 2553), but MS-Windows did not support them natively, prior to + * Win-XP. On earlier Windows versions, they may be supported, (but + * without IPv6 support), by including ; (on Win-2K, IPv6 + * support may be attainable, if the IPv6 Technology Preview kit has + * been installed, and the application is linked with WSHIP6.DLL). + * + * We expose these declarations only if we are compiling for Win-XP + * or later, and then only if has not been included. + */ +WSAAPI int getaddrinfo +(const char *, const char *, const struct addrinfo *, struct addrinfo **); + +WSAAPI int getnameinfo +(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int); -#if (_WIN32_WINNT >= 0x0501) -void WSAAPI freeaddrinfo (struct addrinfo*); -int WSAAPI getaddrinfo (const char*,const char*,const struct addrinfo*, - struct addrinfo**); -int WSAAPI getnameinfo(const struct sockaddr*,socklen_t,char*,DWORD, - char*,DWORD,int); -#else -/* FIXME: Need WS protocol-independent API helpers. */ +WSAAPI void freeaddrinfo (struct addrinfo *); #endif -static __inline char* -gai_strerrorA(int ecode) +#define gai_strerror __AW_SUFFIXED__( gai_strerror ) + +static __inline__ +char *gai_strerrorA (int ecode) { - static char message[1024+1]; - DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_IGNORE_INSERTS - | FORMAT_MESSAGE_MAX_WIDTH_MASK; - DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); - FormatMessageA(dwFlags, NULL, ecode, dwLanguageId, (LPSTR)message, 1024, NULL); - return message; + static char message[1024+1]; + DWORD Flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS + | FORMAT_MESSAGE_MAX_WIDTH_MASK; + DWORD LanguageId = MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT); + FormatMessageA (Flags, NULL, ecode, LanguageId, message, 1024, NULL); + return message; } -static __inline WCHAR* -gai_strerrorW(int ecode) + +static __inline__ +WCHAR *gai_strerrorW (int ecode) { - static WCHAR message[1024+1]; - DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_IGNORE_INSERTS - | FORMAT_MESSAGE_MAX_WIDTH_MASK; - DWORD dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); - FormatMessageW(dwFlags, NULL, ecode, dwLanguageId, (LPWSTR)message, 1024, NULL); - return message; + static WCHAR message[1024+1]; + DWORD Flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS + | FORMAT_MESSAGE_MAX_WIDTH_MASK; + DWORD LanguageId = MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT); + FormatMessageW (Flags, NULL, ecode, LanguageId, message, 1024, NULL); + return message; } -#ifdef UNICODE -#define gai_strerror gai_strerrorW -#else -#define gai_strerror gai_strerrorA -#endif /* Some older IPv4/IPv6 compatibility stuff */ /* This struct lacks sin6_scope_id; retained for use in sockaddr_gen */ -struct sockaddr_in6_old { - short sin6_family; - u_short sin6_port; - u_long sin6_flowinfo; - struct in6_addr sin6_addr; +struct sockaddr_in6_old +{ short sin6_family; + u_short sin6_port; + u_long sin6_flowinfo; + struct in6_addr sin6_addr; }; -typedef union sockaddr_gen{ - struct sockaddr Address; - struct sockaddr_in AddressIn; - struct sockaddr_in6_old AddressIn6; +typedef +union sockaddr_gen +{ struct sockaddr Address; + struct sockaddr_in AddressIn; + struct sockaddr_in6_old AddressIn6; } sockaddr_gen; - -typedef struct _INTERFACE_INFO { - u_long iiFlags; - sockaddr_gen iiAddress; - sockaddr_gen iiBroadcastAddress; - sockaddr_gen iiNetmask; +typedef +struct _INTERFACE_INFO +{ u_long iiFlags; + sockaddr_gen iiAddress; + sockaddr_gen iiBroadcastAddress; + sockaddr_gen iiNetmask; } INTERFACE_INFO, *LPINTERFACE_INFO; -/* - The definition above can cause problems on NT4,prior to sp4. - To workaround, include the following struct and typedef and - #define INTERFACE_INFO OLD_INTERFACE_INFO - See: FIX: WSAIoctl SIO_GET_INTERFACE_LIST Option Problem - (Q181520) in MSDN KB. - - The old definition causes problems on newer NT and on XP. - -typedef struct _OLD_INTERFACE_INFO { - u_long iiFlags; - struct sockaddr iiAddress; - struct sockaddr iiBroadcastAddress; - struct sockaddr iiNetmask; +#if 0 +/* The definition above may cause problems on WinNT4, prior to + * service pack 4. To work around this, if necessary, include + * the following typedef and struct definition, and + * + * #define INTERFACE_INFO OLD_INTERFACE_INFO + * + * See FIX: WSAIoctl SIO_GET_INTERFACE_LIST Option Problem + * (Q181520) in MSDN KB. Do note, however, that exposure of + * the old definition may cause problems on newer variants of + * WinNT, and on WinXP and later. + */ +typedef struct _OLD_INTERFACE_INFO +{ u_long iiFlags; + struct sockaddr iiAddress; + struct sockaddr iiBroadcastAddress; + struct sockaddr iiNetmask; } OLD_INTERFACE_INFO; -*/ - -#ifdef __cplusplus -} -#endif #endif + +_END_C_DECLS + +#endif /* !_WS2TCPIP_H: $RCSfile$: end of file */ diff --git a/w32api/include/wspiapi.h b/w32api/include/wspiapi.h new file mode 100644 index 0000000..0dc7656 --- /dev/null +++ b/w32api/include/wspiapi.h @@ -0,0 +1,980 @@ +/* + * wspiapi.h + * + * Implementation for pre-WinXP getaddrinfo() and getnameinfo() APIs. + * + * + * $Id$ + * + * Written by Keith Marshall + * Copyright (C) 2020, MinGW.org Project + * + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * 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. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * + * Author's Note: + * I have written this implementation of from scratch, based + * entirely on hints from publicly visible Microsoft documentation, my own + * interpretation of IETF's RFC 3493, the corresponding POSIX.1 specification, + * and observation of the behaviour of example code from Microsoft's publicly + * visible documentation, which I have adapted to run on my GNU/Linux host. + * I have never seen code for Microsoft's implementation; nor + * have I copied code from any other implementation, however licensed. + * + * This implementation servs as a fall-back for use on those legacy versions + * of Windows which do not support the getaddrinfo() and getnameinfo() APIs, + * through WS2_32.DLL on WinXP and later, or through WSHIP6.DLL on Win2K + * (with the IPv6 Preview Kit). Since such legacy Windows versions do not + * otherwise support IPv6, there is no attempt to support the AF_INET6 + * address family; AF_INET is supported, as is AF_UNSPEC, but only to + * the extent that it is interpreted as AF_INET alone. + * + * It should be noted that, as described in Microsoft's documentation, the + * fall-back code is implemented in the form of in-line functions. This + * has a potentially disadvantageous side effect, in that every translation + * unit which calls any of the fall-back functions will incur a substantial + * overhead of potentially duplicated fall-back code. To mitigate this, it + * is recommended that all such calls be encapsulated within one translation + * unit, and exposed globally through public wrapper functions; an example + * of this technique may be found in the gcc/ada/socket.c implementation + * within recent GCC sources, wherein the public API is exposed via the + * __gnat_getaddrinfo(), __gnat_freeaddrinfo(), and __gnat_getnameinfo() + * function wrappers. + * + */ +#ifndef _WSPIAPI_H +#pragma GCC system_header +#define _WSPIAPI_H + +#include +#include + +/* Symbolic constants are useful as wild-card identifiers for socket types + * and protocols, but these are non-standard; we don't want to pollute the + * public namespace, so we add the reserved __WSPIAPI_ prefix for these. + */ +#define __WSPIAPI_SOCK_ANY 0 +#define __WSPIAPI_IPPROTO_ANY 0 + +_BEGIN_C_DECLS + +/* __wspiapi_errout(): a helper to assign a WSA error code, via the + * WSASetLastError() function, and also return it for immediate use + * as the exit status for any wspiapi function. + */ +static __inline__ __attribute__((__always_inline__)) +int __wspiapi_errout( int status ){ WSASetLastError( status ); return status; } + +/* __wspiapi_syserrout(): variation on __wspiapi_errout(), returning + * EAI_SYSTEM, after assigning a specific system errno value. + */ +static __inline__ +__attribute__((__always_inline__)) +int __wspiapi_syserrout( int errcode ) +{ errno = errcode; return __wspiapi_errout( EAI_SYSTEM ); } + +/* enum __wspiapi: generate symbolic names for the entry point indices + * within the wspapi function reference dictionary. + */ +enum __wspiapi +{ __WSPIAPI_FREEADDRINFO__, + __WSPIAPI_GETADDRINFO__, + __WSPIAPI_GETNAMEINFO__ +}; + +/* __wspiapi_t: a private use data type, describing the structure of + * each individual function reference within the dictionary. + */ +typedef struct +{ const char *name; + void *entry; +} __wspiapi_t; + +/* __wspiapi_reference(): a helper function to retrieve a function + * entry point address, by indexed function name look-up within the + * reference dictionary. + */ +static __inline__ __attribute__((__always_inline__)) +__wspiapi_t *__wspiapi_reference( enum __wspiapi index ) +{ + /* The dictionary, itself, is statically encapsulated within this + * helper function, (and thus, is not publicly visible). Note that, + * initially, all entry point vectors are specified as (void *)(-1), + * and will be fixed-up on first call to any related function. + */ + static __wspiapi_t dictionary[] = + { { "freeaddrinfo", (void *)(-1) }, + { "getaddrinfo", (void *)(-1) }, + { "getnameinfo", (void *)(-1) } + }; + + /* The return value is simply a pointer to the dictionary entry + * corresponding to the specified index. + */ + return dictionary + index; +} + +/* __wspiapi_set_entry(): helper function to perform the dictionary + * fix-up, for a single function entry point reference, as described + * for the __wspiapi_reference() function above. Note that this is + * called, only after a candidate DLL, which may be expected to + * provide the associated function, has been identified. + */ +static __inline__ __attribute__((__always_inline__)) +void __wspiapi_set_entry( HMODULE dll, __wspiapi_t *api ) +{ api->entry = (void *)(GetProcAddress( dll, api->name )); } + +/* __wspiapi_module_handle(): helper to obtain a module handle for + * a candidate DLL, which is expected to provide all of the wspiapi + * functions, based on it satisfying one dictionary reference. + */ +static __inline__ +HMODULE __wspiapi_module_handle +( char *path, char *subst, const char *name, __wspiapi_t *api ) +{ + HMODULE ref; + + /* "subst" points to the offset, within the "path" buffer, at which + * the system directory path name ends, and the DLL file name should + * be inserted; append the specified DLL file "name", and attempt to + * load a system DLL of that name. + */ + strcpy( subst, name ); + if( (ref = LoadLibraryA( path )) != NULL ) + { + /* We successfully obtained a reference handle for the named DLL; + * check that we can also obtain an entry point reference, within + * this DLL, for the specified wspiapi function, recording the + * result in the wspiapi reference dictionary... + */ + if( (api->entry = (void *)(GetProcAddress( ref, api->name ))) == NULL ) + { + /* ...and declaring no further wspiapi interest in this DLL, if + * the requisite entry point is unavailable. + */ + FreeLibrary( ref ); + return NULL; + } + } + /* If we get to here, then we either have a valid handle for a DLL + * which does provide the requisite wspiapi witness function, in + * which case we return that handle, otherwise we return the NULL + * resulting from a failed DLL load request. + */ + return ref; +} + +/* __wspiapi_lib(): a thin wrapper, through which all calls to + * __wspiapi_module_handle() are directed; it passes a system DLL + * search path, wherein the specified DLL name is substituted, (at + * the substring offset specified by "subst"), requiring that the + * designated system DLL should provide freeaddrinfo() as witness + * that it may be a suitable candidate provider for all of those + * functions which must otherwise be replaced by + * fall-back implementations. + */ +static __inline__ __attribute__((__always_inline__)) +HMODULE __wspiapi_lib( char *path, char *subst, const char *name ) +{ + /* This requires nothing more than appending the wspiapi dictionary + * reference for the freeaddrinfo() function, to the arguments which + * have already been specified, and forwarding the request onward, + * to __wspiapi_module_handle(). + */ + return __wspiapi_module_handle( + path, subst, name, __wspiapi_reference(__WSPIAPI_FREEADDRINFO__) + ); +} + +/* __wspiapi_entry(): helper to retrieve the entry point address for + * a specified wspiapi function, by indexed look-up within the wspiapi + * reference dictionary; invokes __wspiapi_lib(), as may be required, + * to initialize the reference dictionary entries. + */ +static __inline__ +void *__wspiapi_entry( enum __wspiapi index ) +# define __wspiapi_call( index ) (call)(__wspiapi_entry( index )) +{ + /* First, check that the reference dictionary has been initialized, + * using the freeaddrinfo() entry point reference as witness. + */ + if( __wspiapi_reference(__WSPIAPI_FREEADDRINFO__)->entry == (void *)(-1) ) + { + /* The dictionary appears to be uninitialized; set up to perform + * entry point searches within DLLs in the system directory... + */ + HMODULE dll; + char sys_path[MAX_PATH], *dllname; + dllname = sys_path + GetSystemDirectory( sys_path, MAX_PATH - 11 ); + if( dllname > sys_path ) + { /* ...then, preferring WS2_32.DLL, but accepting WSHIP6.DLL as + * a second choice alternative, initialize the dictionary entry + * for the freeaddrinfo() witness function... + */ + if( ((dll = __wspiapi_lib( sys_path, dllname, "\\ws2_32" )) != NULL) + || ((dll = __wspiapi_lib( sys_path, dllname, "\\wship6" )) != NULL) ) + { + /* ...and, when that resolves to a valid entry point address, + * also initialize the entry point references, using the same + * DLL, for the getaddrinfo() and getnameinfo() functions... + */ + __wspiapi_set_entry( dll, __wspiapi_reference(__WSPIAPI_GETADDRINFO__) ); + __wspiapi_set_entry( dll, __wspiapi_reference(__WSPIAPI_GETNAMEINFO__) ); + FreeLibrary( dll ); + } + else + { /* ...otherwise, initialize these remaining references, such + * that all three functions are marked as unsupported by any + * system DLL, and thus must be emulated, on demand, by use + * of in-line code. + */ + __wspiapi_reference(__WSPIAPI_GETADDRINFO__)->entry = NULL; + __wspiapi_reference(__WSPIAPI_GETNAMEINFO__)->entry = NULL; + } + } + } + /* Irrespective of whether initialization was performed on this + * occasion, or had been performed previously, we return the entry + * point address for the requested function, as it is now recorded + * in the wspiapi reference dictionary. + */ + return __wspiapi_reference( index )->entry; +} + +/* __wspiapi_freeaddrinfo(): fall-back implementation for freeaddrinfo(), + * invoked by WspiapiFreeAddrInfo(), if (and only if), there is no native + * implementation provided in WS2_32.DLL, or by way of WSHIP6.DLL, from + * the Win2K IPv6 Technology Preview. + */ +static __inline__ +WSAAPI void __wspiapi_freeaddrinfo( struct addrinfo *ai_chain ) +{ + /* This must free all addrinfo records on a single chain, which has been + * allocated by a prior call of __wspiapi_getaddrinfo(); note that it is + * necessary to free any associated ai_canonname records, which have been + * independently allocated, but not any ai_addr records, as these are all + * allocated as integral elements of the owner addrinfo record itself. + */ + while( ai_chain != NULL ) + { struct addrinfo *next = ai_chain->ai_next; + free( ai_chain->ai_canonname ); free( ai_chain ); + ai_chain = next; + } +} + +/* __wspiapi_addrinfo_cleanup(): a local helper function, called only by + * __wspiapi_getaddrinfo(), to clean up any partially allocated addrinfo + * record chain, in the event of __wspiapi_getaddrinfo() failure. + */ +static __inline__ __attribute__((__always_inline__)) +struct addrinfo *__wspiapi_addrinfo_cleanup( struct addrinfo *ai_chain ) +{ __wspiapi_freeaddrinfo( ai_chain ); return NULL; } + +/* __wspiapi_getaddrinfo_internal(): core implementation for, and called + * exclusively by, __wspiapi_getaddrinfo(); returns zero on success, or + * an EAI error code, (but without updating WSAGetLastError() status), + * on failure. + */ +static __inline__ +__attribute__((__always_inline__)) +int __wspiapi_getaddrinfo_internal +( const char *__restrict__ nodename, const char *__restrict__ servname, + const struct addrinfo *__restrict__ hints, struct addrinfo **__restrict__ res +) +{ /* Either nodename, or servname, but not both, may be NULL; initially + * assign status to reflect the invalid condition of both being NULL. + */ + int status = EAI_NONAME; + + /* Initialize a local template, from which each allocated resultant + * addrinfo record may be derived. + */ + struct ai { struct addrinfo ai; struct sockaddr_in sa; } ai_data; + memset( &ai_data, 0, sizeof( struct ai ) ); + + /* Ensure that the state of the resultant addrinfo record chain will + * be sane, in the event of early failure. + */ + *res = NULL; + + /* The hints may be NULL, (in which case the initial template state + * will prevail), but if specified... + */ + if( hints != NULL ) + { /* ...then its ai_addr, ai_canonname, and ai_next fields MUST all + * be NULL, and ai_addrlen MUST be zero. + */ + if( (hints->ai_addr != NULL) || (hints->ai_addrlen != 0) + || (hints->ai_next != NULL) || (hints->ai_canonname != NULL) ) + return EAI_FAIL; + + /* Only the IPv4 AF_INET ai_family is supported, (but we tolerate + * AF_UNSPEC as implying AF_INET). + */ + switch( ai_data.ai.ai_family = hints->ai_family ) + { case AF_UNSPEC: case AF_INET: break; default: return EAI_FAMILY; } + + /* Protocol and socket type may each be specified explicitly, or + * implicitly, (by default, or by wild-card assignment; supported + * protocols are TCP and UDP... + */ + switch( ai_data.ai.ai_protocol = hints->ai_protocol ) + { + case IPPROTO_TCP: + /* ...where an explicit choice of TCP must be associated with + * a stream type socket... + */ + switch( hints->ai_socktype ) + { /* ...which again, may be either explicitly assigned, or + * implicitly deduced. + */ + case SOCK_STREAM: case __WSPIAPI_SOCK_ANY: + ai_data.ai.ai_socktype = SOCK_STREAM; + break; + + /* Any other explicit pairing results in failure. + */ + default: return EAI_SOCKTYPE; + } + break; + + case IPPROTO_UDP: + /* Similarly, an explicit protocol choice must be paired, + * explicitly or implicitly, with a datagram socket. + */ + switch( hints->ai_socktype ) + { case SOCK_DGRAM: case __WSPIAPI_SOCK_ANY: + ai_data.ai.ai_socktype = SOCK_DGRAM; + break; + + /* Once again, any other explicit pairing is invalid. + */ + default: return EAI_SOCKTYPE; + } + break; + + case __WSPIAPI_IPPROTO_ANY: + /* Without an explicit protocol specification, we may need + * to infer a match for the socket type... + */ + switch( ai_data.ai.ai_socktype = hints->ai_socktype ) + { + /* ...pairing TCP with an explicit stream socket... + */ + case SOCK_STREAM: + ai_data.ai.ai_protocol = IPPROTO_TCP; + break; + + /* ...UDP with a datagram socket... + */ + case SOCK_DGRAM: + ai_data.ai.ai_protocol = IPPROTO_UDP; + + /* ...or fall through, to enable generation of an addrinfo + * list, with all supported valid pairings. + */ + case __WSPIAPI_SOCK_ANY: break; + + /* Bail out, if any unsupported socket type is explicitly + * specified... + */ + default: return EAI_SOCKTYPE; + } + break; + + /* ...and likewise, for any unsupported protocol. + */ + default: return EAI_SOCKTYPE; + } + + /* Simply propagate whatever flags may have been specified, but + * defer any validation of them, until later. + */ + ai_data.ai.ai_flags = hints->ai_flags; + } + + /* Only the IPv4 address family is supported; whether confirmed in + * the hints, or left unspecified, we may stipulate that now. + */ + ai_data.ai.ai_family = ai_data.sa.sin_family = AF_INET; + + /* The servname argument may be NULL, but when it is specified, and + * it is a non-empty string... + */ + if( (servname != NULL) && (*servname != '\0') ) + { /* ...first try to interpret it as a textual representation of a + * service port number... + */ + struct servent *pi; char *brk; + ai_data.sa.sin_port = strtoul( servname, &brk, 0 ); + if( *brk == '\0' ) pi = getservbyport( htons( ai_data.sa.sin_port ), NULL ); + + /* ...or, failing that, a well known service name, which may be + * mapped to such a port number, or else bail out. + */ + else pi = getservbyname( servname, NULL ); + if( pi == NULL ) return EAI_SERVICE; + + /* When the servname argument has been successfully interpreted, + * store the port number within the addrinfo template, and reset + * the return status, to indicate that the minimal requirement + * for at least one of servname and nodename is non-NULL. + */ + ai_data.sa.sin_port = pi->s_port; + status = 0; + } + + /* Similarly, the nodename argument may be NULL, but when it is + * specified, and is non-empty... + */ + if( (nodename != NULL) && (*nodename != '\0') ) + { /* ...then we first attempt to interpret it as a representation, + * in textual format, of an IPv4 address. + */ + unsigned long *addr = (unsigned long *)(&(ai_data.sa.sin_addr)); + if( (*addr = inet_addr( nodename )) != INADDR_NONE ) + { + /* We've interpreted, and stored, a valid IPv4 address; this + * isn't, strictly, a node name; it is valid, in this context, + * but association with a canonical name is disallowed. + */ + if( (ai_data.ai.ai_flags & AI_CANONNAME) == AI_CANONNAME ) + return EAI_BADFLAGS; + + /* When the IPv4 address form is accepted, we may immediately + * reset the return status, since the requirement for at least + * one nodename, or servname, to be non-NULL is satisfied. + */ + status = 0; + } + else if( (ai_data.ai.ai_flags & AI_NUMERICHOST) == AI_NUMERICHOST ) + { + /* No numeric IPv4 address was specified, but with this flag + * one becomes a mandatory requirement; bail out. + */ + return EAI_NONAME; + } + else + { /* The specified nodename could not be interpreted as an IPv4 + * address, (but was not required to be); resolve it as a host + * name, to obtain the corresponding IPv4 address. + */ + struct hostent *hi = gethostbyname( nodename ); + if( hi != NULL ) + { + /* Name resolution was successful; update the template data + * to reflect the size of an IPv4 socket data structure, and + * store the resolved IPv4 address. + * + * FIXME: is it necessary to adapt this, to process a list + * comprising more than one resolved IPv4 address? + */ + ai_data.ai.ai_addrlen = sizeof( struct sockaddr ); + ai_data.sa.sin_addr = **(struct in_addr **)(hi->h_addr_list); + + /* Also, if requested, capture the resolved canonical host + * name, but fail if there is insufficient memory. + */ + if( ((ai_data.ai.ai_flags & AI_CANONNAME) == AI_CANONNAME) + && ((ai_data.ai.ai_canonname = strdup( hi->h_name )) == NULL) ) + return EAI_MEMORY; + + /* On successfully updating the template, to reflect all + * data associated with the resolved nodename, reset the + * exit status to indicate success. + */ + status = 0; + } + } + } + else if( status == 0 ) + { /* The nodename argument was effectively NULL, but we are able + * to proceed due to prior interpretation of a non-NULL servname + * argument; assign default addresses... + */ + if( ((ai_data.ai.ai_flags & AI_PASSIVE) == AI_PASSIVE) ) + { /* ...corresponding to the wild-card address for any passive + * connection... + */ + ai_data.sa.sin_addr.s_addr = INADDR_ANY; + } + else + { /* ...or the loopback address, for non-passive connections. + */ + ai_data.sa.sin_addr.s_addr = INADDR_LOOPBACK; + } + } + + /* Provided the return status code has been reset to zero, from + * its initial EAI_NONAME value... + */ + if( status == 0 ) + { /* ...we should now have a suitably initialized template, from + * which we may construct the linked list of matching addrinfo + * structures to be returned. Each list entry is separately + * allocated, using the local pai pointer to track insertion + * point, selecting protocol and socket type pairings from the + * indexed list, working from TCP (at index two), back towards + * the wild-card protocol match (at index zero), and passing + * through UDP (at index one), until we reach the protocol as + * specified in the template; note that this will generate a + * single entry of TCP, or UDP, when either is specified as an + * explicit match requirement, but will generate three entries, + * matching each of TCP, UDP, and ANY, when a wild-card match + * is specified. + */ + struct ai *pai = NULL; + int ref = (ai_data.ai.ai_protocol == IPPROTO_UDP) ? 1 : 2; + struct { int protocol; int socktype; } map[] = + { { __WSPIAPI_IPPROTO_ANY, __WSPIAPI_SOCK_ANY }, + { IPPROTO_UDP, SOCK_DGRAM }, { IPPROTO_TCP, SOCK_STREAM } + }; + do { if( pai == NULL ) + { /* When pai is in its initial NULL state, the entry to + * be generated may be assigned directly to the head of + * the return list... + */ + pai = ((struct ai *)(malloc( sizeof ai_data ))); + *res = (struct addrinfo *)(pai); + } + /* ...otherwise we link it as ai_next successor to the + * previously allocated entry. + */ + else + { pai->ai.ai_next = (struct addrinfo *)(malloc( sizeof ai_data )); + pai = (struct ai *)(pai->ai.ai_next); + } + if( pai == NULL ) + { /* When pai remains in, or reverts to, the NULL state, + * then allocation of memory for the list entry failed; + * clean up any partially allocated return data, before + * bailing out. + */ + *res = __wspiapi_addrinfo_cleanup( *res ); + return EAI_MEMORY; + } + /* We got a successfully allocated block of memory for a + * list entry; populate it by copying the template... + */ + *pai = ai_data; + + /* ...then fix up the embedded sockaddr data reference, + * its protocol, and the socket type indicators. + */ + pai->ai.ai_addr = (struct sockaddr *)(&(pai->sa.sin_family)); + pai->ai.ai_protocol = map[ref].protocol; + pai->ai.ai_socktype = map[ref].socktype; + + /* The template may initially include a canonical name + * reference; this is propagated to the first list entry + * only, so clear it prior to generating any more. + */ + ai_data.ai.ai_canonname = NULL; + + /* Go round again, until the addrinfo list has become + * fully populated. + */ + } while( map[ref--].protocol != ai_data.ai.ai_protocol ); + } + /* Ultimately, return either the initially assumed EAI_NONAME + * failure status, or success. + */ + return status; +} + +/* __wspiapi_getaddrinfo(): a thin wrapper around the preceding + * in-line implementation; invoked by WspiapiGetAddrInfo() if, and + * only if, no getaddrinfo() implementation is identifiable within + * the host system's WS2_32.DLL, or WSHIP6.DLL + */ +WSAAPI int __wspiapi_getaddrinfo ( + const char *__restrict__ nodenam, const char *__restrict__ servnam, + const struct addrinfo *__restrict__ hints, struct addrinfo **__restrict__ res +) +{ /* First ensure that a non-NULL reference pointer is provided, to + * pass the addrinfo result back to the caller, then delegate the + * call to the in-line handler, capturing its exit status, which + * is then propagated through the WSAGetLastError() API. + */ + if( res == NULL ) return __wspiapi_syserrout( EINVAL ); + { int status = __wspiapi_getaddrinfo_internal( nodenam, servnam, hints, res ); + return (status == 0) ? 0 : __wspiapi_errout( status ); + } +} + +/* WspiapiGetAddrInfo(): getaddrinfo() function redirector, as + * prescribed by Microsoft's on-line documentation. + */ +static __inline__ +WSAAPI int WspiapiGetAddrInfo +( const char *node, const char *service, const struct addrinfo *hints, + struct addrinfo **response_list +) +{ /* Initialized to NULL, the redirector hook will be updated on + * first call, within the containing translation unit. This call + * will determine if it can be directed to an actual getaddrinfo() + * function implementation, provided by the system's WS2_32.DLL or + * WSHIP6.DLL libraries, initializing redirection for this first, + * and any subsequent call, to delegate to any such existing + * implementation, if available... + */ + typedef WSAAPI int (*call) + ( const char *, const char *, const struct addrinfo *, struct addrinfo ** + ); + static call redirector_hook = NULL; + if( (redirector_hook == NULL) + && ((redirector_hook = __wspiapi_call(__WSPIAPI_GETADDRINFO__)) == NULL) ) + + /* ...or otherwise, to substitute the above fallback. + */ + redirector_hook = __wspiapi_getaddrinfo; + + /* Ultimately, every call is redirected to whichever handler has + * been selected by the preceding initialization. + */ + return redirector_hook( node, service, hints, response_list ); +} + +/* getaddrinfo(): a static inline override for any external + * implementation of getaddrinfo(); ensures that corresponding + * function calls, within the translation unit file scope, are + * delegated to the fallback WspiapiGetAddrInfo() redirector. + */ +static __inline__ __attribute__((__always_inline__)) +WSAAPI int getaddrinfo( const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **response_list ) +{ return WspiapiGetAddrInfo( node, service, hints, response_list ); } + +/* WspiapiFreeAddrInfo(): freeaddrinfo() function redirector, as + * prescribed by Microsoft's on-line documentation. + */ +static __inline__ +WSAAPI void WspiapiFreeAddrInfo( struct addrinfo *ai_chain ) +{ + /* Initialized to NULL, the redirector hook will be updated on + * first call, within the containing translation unit. This call + * will determine if it can be directed to an actual freeaddrinfo() + * function implementation, provided by the system's WS2_32.DLL or + * WSHIP6.DLL libraries, initializing redirection for this first, + * and any subsequent call, to delegate to any such existing + * implementation, if available... + */ + typedef WSAAPI void (*call)(struct addrinfo *); + static call redirector_hook = NULL; + if( (redirector_hook == NULL) + && ((redirector_hook = __wspiapi_call(__WSPIAPI_FREEADDRINFO__)) == NULL) ) + + /* ...or otherwise, to substitute the above fallback. + */ + redirector_hook = __wspiapi_freeaddrinfo; + + /* Ultimately, every call is redirected to whichever handler has + * been selected by the preceding initialization. + */ + redirector_hook( ai_chain ); +} + +/* freeaddrinfo(): a static inline override for any external + * implementation of freeaddrinfo(); ensures that corresponding + * function calls, within the translation unit file scope, are + * delegated to the fallback WspiapiFreeAddrInfo() redirector. + */ +static __inline__ __attribute__((__always_inline__)) +WSAAPI void freeaddrinfo (struct addrinfo *ai_chain) +{ WspiapiFreeAddrInfo (ai_chain); } + +/* __wspiapi_getservname(): helper to retrieve the standard name, + * if any, for the service nominally associated with any port which + * is specified by its number in network byte order. + */ +static __inline__ __attribute__((__always_inline__)) +char *__wspiapi_getservname( u_short port, int flags, char *service, + socklen_t servlen, socklen_t *retlen +) +{ /* This look-up is suppressed, if NI_NUMERICSERV is included + * within the specified flags... + */ + if( (flags & NI_NUMERICSERV) == 0 ) + { + /* ...but, when allowed to proceed, we use the legacy windows + * getservbyport() function to perform the look-up... + */ + struct servent *service_info; + service_info = getservbyport( port, (flags & NI_DGRAM) ? "udp" : NULL ); + + /* ...and then, provided a valid service name has been found, + * we transcribe it into the supplied buffer, for return. + */ + if( (service_info != NULL) && (service_info->s_name != NULL) ) + { *retlen = snprintf( service, servlen, "%s", service_info->s_name ); + return service; + } + } + /* If, for whatever reason, no service name is to be returned, + * returning NULL will signal this. + */ + return NULL; +} + +/* __wspiapi_getnodename(): helper to perform a reverse DNS look-up, + * to retrieve, and nominally return, the fully qualified domain name + * which is associated with a specified IPv4 address. + */ +static __inline__ __attribute__((__always_inline__)) +char *__wspiapi_getnodename( struct in_addr *addr, int flags, + char *nodename, socklen_t nodelen, socklen_t *retlen +) +{ /* This look-up is suppressed, when NI_NUMERICHOST is included + * within the specified flags... + */ + if( (flags & NI_NUMERICHOST) == 0 ) + { + /* ...but, when allowed to proceed, we use the legacy windows + * gethostbyaddr() function to perform the look-up... + */ + struct hostent *node_info = gethostbyaddr( + (const char *)(addr), sizeof( struct in_addr ), AF_INET + ); + + /* ...and then, provided a valid host name has been found, + * we transcribe it into the supplied buffer, for return. + */ + if( (node_info != NULL) && (node_info->h_name != NULL) ) + { *retlen = snprintf( nodename, nodelen, "%s", node_info->h_name ); + + /* A special case arises, when NI_NOFQDN is included + * within the specified flags. + */ + if( (flags & NI_NOFQDN) != 0 ) + { + /* In this case, we must check for any period, as + * punctuation separating the host name itself from + * its domain name... + */ + char *brk = strchr( nodename, '.' ); + if( brk != NULL ) + { + /* ...and, when such punctuation is present, we + * truncate the return string at the first period, + * so leaving only the bare host name for return. + */ + *brk = '\0'; + *retlen = brk - nodename; + } + } + /* Truncated, or otherwise, we may now return the host + * name, as retrieved. + */ + return nodename; + } + } + /* If, for whatever reason, no host name is to be returned, + * returning NULL will signal this. + */ + return NULL; +} + +/* __wspiapi_getnameinfo_internal(): core implementation for, and called + * exclusively by, __wspiapi_getnameinfo(); returns zero on success, or + * an EAI error code, (but without updating WSAGetLastError() status), + * on failure. + */ +static __inline__ +__attribute__((__always_inline__)) +int __wspiapi_getnameinfo_internal +( const struct sockaddr *__restrict__ sa, socklen_t len, + char *__restrict__ node, socklen_t nodelen, char *__restrict__ service, + socklen_t servlen, int flags +) +{ /* Either the service name, or the node name look-up may be omitted, + * but at least one MUST be performed; if neither is performed, as we + * may establish later, then we should fail with status EAI_NONAME. + */ + int status = EAI_NONAME; + + /* If support for IPv6 is available, then the system DLL will provide + * its own getnameinfo() implementation, and this fallback handler is + * not invoked; thus, we need only support the IPv4 address family. + */ + if( sa->sa_family != AF_INET ) return EAI_FAMILY; + + /* The "service" argument may be NULL, or its associated "servlen" + * may be zero; a service name look-up is performed, only if neither + * of these excluding conditions is satisfied. + */ + if( (service != NULL) && (servlen > 0) ) + { + /* When look-up is NOT excluded, we delegate it to our internal + * __wspiapi_getservname() helper, capturing the result into an + * intermediate buffer of the same length as has been declared + * for the returned result... + */ + char servname[servlen]; + u_short port = ((const struct sockaddr_in *)(sa))->sin_port; + if( __wspiapi_getservname( port, flags, servname, servlen, &len ) == NULL ) + + /* ...while noting that, if the look-up was unsuccessful, (or it + * was suppressed, because NI_NUMERICSERV was included within the + * specified flags), we substitute a string representation of the + * service port number. + */ + len = snprintf( servname, servlen, "%u", ntohs( port ) ); + + /* Provided the declared buffer length is sufficient to accommodate + * the result of the look-up, we copy that result from intermediate + * storage to the designated return buffer... + */ + if( servlen > len ) strcpy( service, servname ); + + /* ...but if it doesn't fit, we bail out, reporting overflow. + */ + else return EAI_OVERFLOW; + + /* We HAVE now performed a successful service name look-up, so we + * may clear our initial EAI_NONAME presumption. + */ + status = 0; + } + + /* The "node" argument may be NULL, or its associated "nodelen" may + * be zero; a node name look-up is performed, only if neither of + * these excluding conditions is satisfied. + */ + if( (node != NULL) && (nodelen > 0) ) + { + /* Perform the node name look-up, based on the IPv4 address as + * specified in the "sa" structure argument... + */ + char nodename[nodelen]; + struct in_addr addr = ((const struct sockaddr_in *)(sa))->sin_addr; + if( __wspiapi_getnodename( &addr, flags, nodename, nodelen, &len ) == NULL ) + { + /* ...but on look-up failure, and if not required to succeed, + * (by specification of the NI_NAMEREQD flag)... + */ + if( (flags & NI_NAMEREQD) != 0 ) return EAI_NONAME; + + /* ...substitute the textual representation of the address. + */ + len = snprintf( nodename, nodelen, "%s", inet_ntoa( addr ) ); + } + /* Whether the name look-up was successful, or the address was + * substituted, if the return "node" buffer size is sufficient, + * copy the look-up result into place... + */ + if( nodelen > len ) strcpy( node, nodename ); + + /* ...otherwise, bail out. + */ + else return EAI_OVERFLOW; + + /* If we get this far, the function has completed successfully. + */ + return 0; + } + /* If we didn't perform "node" name look-up, then the return state + * must be either the result of successful "service" resolution, or + * the initially assumed failure state. + */ + return status; +} + +/* __wspiapi_getnameinfo(): a thin wrapper around the preceding + * in-line implementation; invoked by WspiapiGetNameInfo() if, and + * only if, no getnameinfo() implementation is identifiable within + * the host system's WS2_32.DLL, or WSHIP6.DLL + */ +static __inline__ +WSAAPI int __wspiapi_getnameinfo +( const struct sockaddr *__restrict__ sa, socklen_t len, + char *__restrict__ node, socklen_t nodelen, char *__restrict__ service, + socklen_t servlen, int flags +) +{ /* The "sa" argument MUST NOT be NULL, and its declared length MUST + * match that of struct sockaddr. RFC 3493 doesn't prescribe how we + * should handle this case, but we choose to emulate the behaviour of + * Microsoft's invalid parameter handler, while reporting it as an + * EAI_SYSTEM exception. + */ + if( (sa == NULL) || (len < sizeof( struct sockaddr )) ) + return __wspiapi_syserrout( EINVAL ); + + { /* When the "sa" argument is valid, we delegate the call to the + * preceding in-line handler, simply capturing its return status, + * and propagating it through the WSAGetLastError() API, as may + * be appropriate. + */ + int status = __wspiapi_getnameinfo_internal + ( sa, len, node, nodelen, service, servlen, flags ); + return (status == 0) ? 0 : __wspiapi_errout( status ); + } +} + +/* WspiapiGetNameInfo(): getnameinfo() function redirector, as + * prescribed by Microsoft's on-line documentation. + */ +static __inline__ +WSAAPI int WspiapiGetNameInfo +( const struct sockaddr *__restrict__ addr, socklen_t len, + char *__restrict__ node, socklen_t nodelen, char *__restrict__ service, + socklen_t servlen, int flags +) +{ /* Initialized to NULL, the redirector hook will be updated on + * first call, within the containing translation unit. This call + * will determine if it can be directed to an actual getnameinfo() + * function implementation, provided by the system's WS2_32.DLL or + * WSHIP6.DLL libraries, initializing redirection for this first, + * and any subsequent call, to delegate to any such existing + * implementation, if available... + */ + typedef WSAAPI int (*call) + (const SOCKADDR *, socklen_t, char *, socklen_t, char *, socklen_t, int + ); + static call redirector_hook = NULL; + if( (redirector_hook == NULL) + && ((redirector_hook = __wspiapi_call(__WSPIAPI_GETNAMEINFO__)) == NULL) ) + + /* ...or otherwise, to substitute the above fallback. + */ + redirector_hook = __wspiapi_getnameinfo; + + /* Ultimately, every call is redirected to whichever handler has + * been selected by the preceding initialization. + */ + return redirector_hook( addr, len, node, nodelen, service, servlen, flags ); +} + +/* getnameinfo(): a static inline override for any external + * implementation of getnameinfo(); ensures that corresponding + * function calls, within the translation unit file scope, are + * delegated to the fallback WspiapiGetNameInfo() redirector. + */ +static __inline__ __attribute__((__always_inline__)) +WSAAPI int getnameinfo( const SOCKADDR *addr, socklen_t addrlen, + char *node, socklen_t nb_size, char *service, socklen_t sb_size, int flags +) +{ return WspiapiGetNameInfo + (addr, addrlen, node, nb_size, service, sb_size, flags); +} + +_END_C_DECLS + +#endif /* !_WSPIAPI_H: $RCSfile$: end of file */ diff --git a/w32api/tests/headers.at b/w32api/tests/headers.at index 417d25a..d17a92e 100644 --- a/w32api/tests/headers.at +++ b/w32api/tests/headers.at @@ -265,6 +265,7 @@ ws2tcpip.h dnl wsahelp.h dnl wsipx.h dnl wsnetbs.h dnl +wspiapi.h dnl wtsapi32.h dnl wtypes.h dnl xprtdefs.h dnl -- 2.11.0