From c82ec9e4b95c57fa25f5d72ddd2c9191694df89c Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 24 Dec 2023 14:17:55 +1100 Subject: [PATCH] Add support for resolving domains to IPv6 addresses on Windows, also support IPV6 addresses for host component of a URL in HttpClient http backend --- src/Http_Worker.c | 18 +++++++++++++-- src/Platform_Windows.c | 50 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/Http_Worker.c b/src/Http_Worker.c index 1678fc3dc..6e97da177 100644 --- a/src/Http_Worker.c +++ b/src/Http_Worker.c @@ -512,6 +512,21 @@ struct HttpConnection { cc_bool valid; }; +static void ExtractHostPort(const struct HttpUrl* url, cc_string* host, cc_string* port) { + /* address can have the form of either "host" or "host:port" */ + /* Slightly more complicated because IPv6 hosts can be e.g. [::1] */ + cc_string* str = &url->address; + int idx = String_LastIndexOf(str, ':'); + + if (idx == -1) { + *host = *str; + *port = String_Empty; + } else { + *host = String_UNSAFE_Substring(str, 0, idx); + *port = String_UNSAFE_SubstringAt(str, idx + 1); + } +} + static cc_result HttpConnection_Open(struct HttpConnection* conn, const struct HttpUrl* url) { cc_string host, port; cc_uint16 portNum; @@ -519,8 +534,7 @@ static cc_result HttpConnection_Open(struct HttpConnection* conn, const struct H cc_sockaddr addrs[SOCKET_MAX_ADDRS]; int numValidAddrs; - /* address can be either "host" or "host:port" */ - String_UNSAFE_Separate(&url->address, ':', &host, &port); + ExtractHostPort(url, &host, &port); if (!Convert_ParseUInt16(&port, &portNum)) { portNum = url->https ? 443 : 80; } diff --git a/src/Platform_Windows.c b/src/Platform_Windows.c index bfd40ecd5..070bb0fc5 100644 --- a/src/Platform_Windows.c +++ b/src/Platform_Windows.c @@ -416,6 +416,9 @@ static int (WSAAPI *_select)(int nfds, fd_set* readfds, fd_set* writefds, fd_set static struct hostent* (WSAAPI *_gethostbyname)(const char* name); static unsigned short (WSAAPI *_htons)(u_short hostshort); +static int (WSAAPI *_getaddrinfo )(PCSTR nodeName, PCSTR serviceName, const ADDRINFOA* hints, PADDRINFOA* result); +static void (WSAAPI* _freeaddrinfo)(PADDRINFOA addrInfo); + static INT WSAAPI FallbackParseAddress(LPWSTR addressString, INT addressFamily, LPVOID protocolInfo, LPVOID address, LPINT addressLength) { @@ -445,6 +448,7 @@ static void LoadWinsockFuncs(void) { DynamicLib_Sym(connect), DynamicLib_Sym(shutdown), DynamicLib_Sym(ioctlsocket), DynamicLib_Sym(getsockopt), DynamicLib_Sym(gethostbyname), DynamicLib_Sym(htons), + DynamicLib_Sym(getaddrinfo), DynamicLib_Sym(freeaddrinfo), DynamicLib_Sym(recv), DynamicLib_Sym(send), DynamicLib_Sym(select) }; static const cc_string winsock1 = String_FromConst("wsock32.DLL"); @@ -459,7 +463,7 @@ static void LoadWinsockFuncs(void) { if (!_WSAStringToAddressW) _WSAStringToAddressW = FallbackParseAddress; } -static cc_result ParseHost(char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { +static cc_result ParseHostOld(char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { struct hostent* res; cc_result wsa_res; SOCKADDR_IN* addr4; @@ -496,6 +500,44 @@ static cc_result ParseHost(char* host, int port, cc_sockaddr* addrs, int* numVal return i == 0 ? ERR_INVALID_ARGUMENT : 0; } +static cc_result ParseHostNew(char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { + char portRaw[32]; cc_string portStr; + struct addrinfo hints = { 0 }; + struct addrinfo* result; + struct addrinfo* cur; + int res, i = 0; + + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + String_InitArray(portStr, portRaw); + String_AppendInt(&portStr, port); + portRaw[portStr.length] = '\0'; + + res = _getaddrinfo(host, portRaw, &hints, &result); + if (res == EAI_NONAME) return SOCK_ERR_UNKNOWN_HOST; + if (res) return res; + + /* Prefer IPv4 addresses first */ + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family != AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + for (cur = result; cur && i < SOCKET_MAX_ADDRS; cur = cur->ai_next) + { + if (cur->ai_family == AF_INET) continue; + Mem_Copy(addrs[i].data, cur->ai_addr, cur->ai_addrlen); + addrs[i].size = cur->ai_addrlen; i++; + } + + _freeaddrinfo(result); + *numValidAddrs = i; + return i == 0 ? ERR_INVALID_ARGUMENT : 0; +} + cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numValidAddrs) { SOCKADDR_IN* addr4 = (SOCKADDR_IN* )addrs[0].data; SOCKADDR_IN6* addr6 = (SOCKADDR_IN6*)addrs[0].data; @@ -523,7 +565,11 @@ cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* a return 0; } - return ParseHost(str.ansi, port, addrs, numValidAddrs); + if (_getaddrinfo) { + return ParseHostNew(str.ansi, port, addrs, numValidAddrs); + } else { + return ParseHostOld(str.ansi, port, addrs, numValidAddrs); + } } cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) {