[PATCH v2 0/7] MR10794: wininet: Attempt connecting to multiple host addresses and don't forcefully prefer ipv4.
-- v2: wininet: Do not force ipv4 addresses. wininet: Try to connect to multiple server addresses. wininet: Introduce and use create_connect_socket(). wininet: Enclose ipv6 address string in square brackets. wininet: Store server address related data in the new server_addr_t structure. https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/tests/http.c | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c index 42d40dc26e3..a7a0a6bb75a 100644 --- a/dlls/wininet/tests/http.c +++ b/dlls/wininet/tests/http.c @@ -30,6 +30,7 @@ #include "wininet.h" #include "winineti.h" #include "winsock2.h" +#include "ws2tcpip.h" #include "wine/test.h" @@ -6812,6 +6813,97 @@ static void test_pac(int port) close_request(&req); } +struct af_priority_callback_data +{ + int resolving_count; + char *resolving; + int resolved_count; + char *resolved; + int connecting_count; + char *connecting[64]; + int connected_count; + char *connected; +}; + +static VOID WINAPI af_priority_callback(HINTERNET hi, DWORD_PTR context, DWORD status, + void *info, DWORD info_len) +{ + struct af_priority_callback_data *data = (struct af_priority_callback_data *)context; + + ok(!!data, "got NULL.\n"); + + switch (status) + { + case INTERNET_STATUS_RESOLVING_NAME: + ++data->resolving_count; + data->resolving = strdup(info); + break; + case INTERNET_STATUS_NAME_RESOLVED: + ++data->resolved_count; + data->resolved = strdup(info); + break; + case INTERNET_STATUS_CONNECTING_TO_SERVER: + ok(data->connecting_count < ARRAY_SIZE(data->connecting), "got %d.\n", data->connecting_count); + data->connecting[data->connecting_count++] = strdup(info); + break; + case INTERNET_STATUS_CONNECTED_TO_SERVER: + ++data->connected_count; + data->connected = strdup(info); + break; + } +} + +static void test_af_priority(int port) +{ + struct af_priority_callback_data data = { 0 }; + HINTERNET ses, con, req; + struct addrinfo *addr_info, hints; + int i, ret; + BOOL bret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + ret = getaddrinfo("localhost", NULL, &hints, &addr_info); + ok(!ret, "got error %d.\n", ret); + if (addr_info->ai_family != AF_INET6) + { + skip("AF_INET6 not available for localhost.\n"); + freeaddrinfo(addr_info); + return; + } + freeaddrinfo(addr_info); + + ses = InternetOpenA( "winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0 ); + ok( ses != NULL, "InternetOpenA failed\n" ); + pInternetSetStatusCallbackA(ses, &af_priority_callback); + + con = InternetConnectA(ses, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&data); + ok( con != NULL, "InternetConnectA failed %lu\n", GetLastError() ); + req = HttpOpenRequestA(con, "GET", "/test1", NULL, NULL, NULL, 0, (DWORD_PTR)&data); + ok( req != NULL, "HttpOpenRequestA failed %lu\n", GetLastError()); + bret = HttpSendRequestA(req, NULL, 0, NULL, 0); + ok(bret, "HttpSendRequest failed with error %lu\n", GetLastError()); + + ok(data.resolving_count == 1, "got %d.\n", data.resolving_count); + ok(data.resolving && !strcmp(data.resolving, "localhost"), "got %s.\n", debugstr_a(data.resolving)); + free(data.resolving); + ok(data.resolved_count == 1, "got %d.\n", data.resolved_count); + todo_wine ok(data.resolved && !strcmp(data.resolved, "[::1]"), "got %s.\n", debugstr_a(data.resolved)); + free(data.resolved); + todo_wine ok(data.connecting_count == 2, "got %d.\n", data.connecting_count); + todo_wine ok(data.connecting[0] && !strcmp(data.connecting[0], "[::1]"), "got %s.\n", debugstr_a(data.connecting[0])); + todo_wine ok(data.connecting[1] && !strcmp(data.connecting[1], "127.0.0.1"), "got %s.\n", debugstr_a(data.connecting[1])); + for (i = 0; i < ARRAY_SIZE(data.connecting); ++i) + free(data.connecting[i]); + ok(data.connected_count == 1, "got %d.\n", data.connected_count); + ok(data.connected && !strcmp(data.connected, "127.0.0.1"), "got %s.\n", debugstr_a(data.connected)); + free(data.connected); + + InternetCloseHandle(req); + InternetCloseHandle(con); + InternetCloseHandle(ses); +} + static void test_http_connection(void) { struct server_info si; @@ -6832,6 +6924,7 @@ static void test_http_connection(void) return; } + test_af_priority(si.port); test_basic_request(si.port, "GET", "/test1"); test_proxy_indirect(si.port); test_proxy_direct(si.port); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> If proxy is present request->server->addr_str is empty string. --- dlls/wininet/http.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index eb486d8eb29..dd4a2193ac1 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -4911,6 +4911,7 @@ static void http_process_keep_alive(http_request_t *req) static DWORD open_http_connection(http_request_t *request, BOOL *reusing) { + server_t *server; netconn_t *netconn = NULL; DWORD res; @@ -4959,13 +4960,13 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name), request->proxy ? debugstr_w(request->proxy->name) : "(null)"); - + server = request->proxy ? request->proxy : request->server; INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, - request->server->addr_str, - strlen(request->server->addr_str)+1); + server->addr_str, + strlen(server->addr_str)+1); - res = create_netconn(request->proxy ? request->proxy : request->server, request->security_flags, + res = create_netconn(server, request->security_flags, (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0, request->hdr.connect_timeout, &netconn); if(res != ERROR_SUCCESS) { @@ -4977,7 +4978,7 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, - request->server->addr_str, strlen(request->server->addr_str)+1); + server->addr_str, strlen(server->addr_str)+1); *reusing = FALSE; TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/ftp.c | 19 +++++++++---------- dlls/wininet/http.c | 19 +++++++++---------- dlls/wininet/internet.c | 13 ++++++------- dlls/wininet/internet.h | 14 +++++++++----- dlls/wininet/netconnection.c | 6 +++--- dlls/wininet/utility.c | 33 +++++++++++++++------------------ 6 files changed, 51 insertions(+), 53 deletions(-) diff --git a/dlls/wininet/ftp.c b/dlls/wininet/ftp.c index f3f586cd6e1..172483b1b1e 100644 --- a/dlls/wininet/ftp.c +++ b/dlls/wininet/ftp.c @@ -2428,12 +2428,11 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext, DWORD dwInternalFlags) { - struct sockaddr_in socketAddr; INT nsocket = -1; socklen_t sock_namelen; BOOL bSuccess = FALSE; ftp_session_t *lpwfs = NULL; - char szaddr[INET6_ADDRSTRLEN]; + server_addr_t *server_addr; TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n", hIC, debugstr_w(lpszServerName), @@ -2520,22 +2519,21 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME, (LPWSTR) lpszServerName, (lstrlenW(lpszServerName)+1) * sizeof(WCHAR)); - sock_namelen = sizeof(socketAddr); - if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr)) + if (!(server_addr = GetAddress(lpszServerName, lpwfs->serverport))) { INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED); goto lerror; } - if (socketAddr.sin_family != AF_INET) + if (server_addr->addr.ss_family != AF_INET) { - WARN("unsupported address family %d\n", socketAddr.sin_family); + WARN("unsupported address family %d\n", server_addr->addr.ss_family); INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); goto lerror; } INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED, - szaddr, strlen(szaddr)+1); + server_addr->addr_str, strlen(server_addr->addr_str)+1); init_winsock(); nsocket = socket(AF_INET,SOCK_STREAM,0); @@ -2546,9 +2544,9 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, } INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, - szaddr, strlen(szaddr)+1); + server_addr->addr_str, strlen(server_addr->addr_str)+1); - if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0) + if (connect(nsocket, (struct sockaddr *)&server_addr->addr, server_addr->addr_len) < 0) { ERR("Unable to connect (%d)\n", WSAGetLastError()); INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); @@ -2559,7 +2557,7 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, TRACE("Connected to server\n"); lpwfs->sndSocket = nsocket; INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, - szaddr, strlen(szaddr)+1); + server_addr->addr_str, strlen(server_addr->addr_str)+1); sock_namelen = sizeof(lpwfs->socketAddress); getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen); @@ -2572,6 +2570,7 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, } lerror: + free(server_addr); if (!bSuccess) { WININET_Release(&lpwfs->hdr); diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index dd4a2193ac1..ffb75b8b8f7 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -151,6 +151,7 @@ void server_release(server_t *server) CertFreeCertificateChain(server->cert_chain); free(server->name); free(server->scheme_host_port); + free(server->addr); free(server); } @@ -1760,9 +1761,8 @@ static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_req static DWORD HTTP_ResolveName(http_request_t *request) { server_t *server = request->proxy ? request->proxy : request->server; - int addr_len; - if(server->addr_len) + if(server->addr) return ERROR_SUCCESS; INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, @@ -1770,16 +1770,14 @@ static DWORD HTTP_ResolveName(http_request_t *request) server->name, (lstrlenW(server->name)+1) * sizeof(WCHAR)); - addr_len = sizeof(server->addr); - if (!GetAddress(server->name, server->port, (SOCKADDR*)&server->addr, &addr_len, server->addr_str)) + if (!(server->addr = GetAddress(server->name, server->port))) return ERROR_INTERNET_NAME_NOT_RESOLVED; - server->addr_len = addr_len; INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_NAME_RESOLVED, - server->addr_str, strlen(server->addr_str)+1); + server->addr->addr_str, strlen(server->addr->addr_str)+1); - TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str); + TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr->addr_str); return ERROR_SUCCESS; } @@ -4961,10 +4959,11 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) TRACE("connecting to %s, proxy %s\n", debugstr_w(request->server->name), request->proxy ? debugstr_w(request->proxy->name) : "(null)"); server = request->proxy ? request->proxy : request->server; + assert(server->addr); INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, - server->addr_str, - strlen(server->addr_str)+1); + server->addr->addr_str, + strlen(server->addr->addr_str)+1); res = create_netconn(server, request->security_flags, (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0, @@ -4978,7 +4977,7 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, - server->addr_str, strlen(server->addr_str)+1); + server->addr->addr_str, strlen(server->addr->addr_str)+1); *reusing = FALSE; TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn); diff --git a/dlls/wininet/internet.c b/dlls/wininet/internet.c index aee0b096cc6..2fc1cbc08b9 100644 --- a/dlls/wininet/internet.c +++ b/dlls/wininet/internet.c @@ -4176,28 +4176,27 @@ BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwRe if (dwFlags & FLAG_ICC_FORCE_CONNECTION) { - struct sockaddr_storage saddr; - int sa_len = sizeof(saddr); + struct server_addr *addr; WCHAR *host_z; int fd; - BOOL b; host_z = strndupW(host, host_len); if (!host_z) return FALSE; - b = GetAddress(host_z, port, (struct sockaddr *)&saddr, &sa_len, NULL); + addr = GetAddress(host_z, port); free(host_z); - if(!b) + if(!addr) goto End; init_winsock(); - fd = socket(saddr.ss_family, SOCK_STREAM, 0); + fd = socket(addr->addr.ss_family, SOCK_STREAM, 0); if (fd != -1) { - if (connect(fd, (struct sockaddr *)&saddr, sa_len) == 0) + if (connect(fd, (struct sockaddr *)&addr->addr, addr->addr_len) == 0) rc = TRUE; closesocket(fd); } + free(addr); } else { diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h index 0e867529232..31b447ab638 100644 --- a/dlls/wininet/internet.h +++ b/dlls/wininet/internet.h @@ -31,14 +31,18 @@ extern HMODULE WININET_hModule; -typedef struct { - WCHAR *name; - INTERNET_PORT port; - BOOL is_https; +typedef struct server_addr +{ struct sockaddr_storage addr; int addr_len; char addr_str[INET6_ADDRSTRLEN]; +} server_addr_t; +typedef struct { + WCHAR *name; + INTERNET_PORT port; + BOOL is_https; + server_addr_t *addr; WCHAR *scheme_host_port; const WCHAR *host_port; const WCHAR *canon_host_port; @@ -380,7 +384,7 @@ DWORD HTTP_Connect(appinfo_t*,LPCWSTR, LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext, DWORD dwInternalFlags, HINTERNET*); -BOOL GetAddress(const WCHAR*,INTERNET_PORT,SOCKADDR*,int*,char*); +server_addr_t *GetAddress(const WCHAR*,INTERNET_PORT); DWORD get_cookie_header(const WCHAR*,const WCHAR*,WCHAR**); DWORD set_cookie(substr_t,substr_t,substr_t,substr_t,DWORD); diff --git a/dlls/wininet/netconnection.c b/dlls/wininet/netconnection.c index 06aa5c82f6c..5cf37e0d08f 100644 --- a/dlls/wininet/netconnection.c +++ b/dlls/wininet/netconnection.c @@ -294,11 +294,11 @@ static DWORD create_netconn_socket(server_t *server, netconn_t *netconn, DWORD t init_winsock(); - assert(server->addr_len); - result = netconn->socket = socket(server->addr.ss_family, SOCK_STREAM, 0); + assert(server->addr); + result = netconn->socket = socket(server->addr->addr.ss_family, SOCK_STREAM, 0); if(result != -1) { set_socket_blocking(netconn, FALSE); - result = connect(netconn->socket, (struct sockaddr*)&server->addr, server->addr_len); + result = connect(netconn->socket, (struct sockaddr*)&server->addr->addr, server->addr->addr_len); if(result == -1) { res = WSAGetLastError(); diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c index 8d621d39c7d..841bcddc297 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -39,10 +39,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet); -BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, struct sockaddr *psa, int *sa_len, char *addr_str) +server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) { + struct sockaddr_storage *addr; + server_addr_t *server_addr; ADDRINFOW *res, hints; - void *addr = NULL; + void *ip_addr = NULL; int ret; TRACE("%s\n", debugstr_w(name)); @@ -63,33 +65,28 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, struct sockaddr *psa, int if (ret != 0) { TRACE("failed to get address of %s\n", debugstr_w(name)); - return FALSE; + return NULL; } - if (*sa_len < res->ai_addrlen) - { - WARN("address too small\n"); - FreeAddrInfoW(res); - return FALSE; - } - *sa_len = res->ai_addrlen; - memcpy( psa, res->ai_addr, res->ai_addrlen ); + server_addr = malloc(sizeof(*server_addr)); + addr = &server_addr->addr; + server_addr->addr_len = res->ai_addrlen; + memcpy( addr, res->ai_addr, res->ai_addrlen ); /* Copy port */ switch (res->ai_family) { case AF_INET: - addr = &((struct sockaddr_in *)psa)->sin_addr; - ((struct sockaddr_in *)psa)->sin_port = htons(port); + ip_addr = &((struct sockaddr_in *)addr)->sin_addr; + ((struct sockaddr_in *)addr)->sin_port = htons(port); break; case AF_INET6: - addr = &((struct sockaddr_in6 *)psa)->sin6_addr; - ((struct sockaddr_in6 *)psa)->sin6_port = htons(port); + ip_addr = &((struct sockaddr_in6 *)addr)->sin6_addr; + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); break; } - if(addr_str) - inet_ntop(res->ai_family, addr, addr_str, INET6_ADDRSTRLEN); + inet_ntop(res->ai_family, ip_addr, server_addr->addr_str, INET6_ADDRSTRLEN); FreeAddrInfoW(res); - return TRUE; + return server_addr; } /* -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/utility.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c index 841bcddc297..825821663f6 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -44,7 +44,7 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) struct sockaddr_storage *addr; server_addr_t *server_addr; ADDRINFOW *res, hints; - void *ip_addr = NULL; + unsigned int len; int ret; TRACE("%s\n", debugstr_w(name)); @@ -75,16 +75,19 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) switch (res->ai_family) { case AF_INET: - ip_addr = &((struct sockaddr_in *)addr)->sin_addr; ((struct sockaddr_in *)addr)->sin_port = htons(port); + inet_ntop(res->ai_family, &((struct sockaddr_in *)addr)->sin_addr, server_addr->addr_str, INET6_ADDRSTRLEN); break; case AF_INET6: - ip_addr = &((struct sockaddr_in6 *)addr)->sin6_addr; ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + server_addr->addr_str[0] = '['; + inet_ntop(res->ai_family, &((struct sockaddr_in6 *)addr)->sin6_addr, server_addr->addr_str + 1, INET6_ADDRSTRLEN - 2); + len = strlen(server_addr->addr_str); + server_addr->addr_str[len] = ']'; + server_addr->addr_str[len + 1] = 0; break; } - inet_ntop(res->ai_family, ip_addr, server_addr->addr_str, INET6_ADDRSTRLEN); FreeAddrInfoW(res); return server_addr; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/ftp.c | 23 +--------------- dlls/wininet/http.c | 12 +-------- dlls/wininet/internet.c | 5 ++-- dlls/wininet/internet.h | 3 ++- dlls/wininet/netconnection.c | 48 ++++++--------------------------- dlls/wininet/utility.c | 52 ++++++++++++++++++++++++++++++++++++ 6 files changed, 66 insertions(+), 77 deletions(-) diff --git a/dlls/wininet/ftp.c b/dlls/wininet/ftp.c index 172483b1b1e..75d742d35da 100644 --- a/dlls/wininet/ftp.c +++ b/dlls/wininet/ftp.c @@ -2525,40 +2525,19 @@ HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName, goto lerror; } - if (server_addr->addr.ss_family != AF_INET) - { - WARN("unsupported address family %d\n", server_addr->addr.ss_family); - INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); - goto lerror; - } - INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED, server_addr->addr_str, strlen(server_addr->addr_str)+1); init_winsock(); - nsocket = socket(AF_INET,SOCK_STREAM,0); - if (nsocket == -1) - { - INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); - goto lerror; - } - - INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, - server_addr->addr_str, strlen(server_addr->addr_str)+1); - - if (connect(nsocket, (struct sockaddr *)&server_addr->addr, server_addr->addr_len) < 0) + if ((nsocket = create_connect_socket(server_addr, AF_INET, INFINITE, &hIC->hdr, dwContext)) < 0) { ERR("Unable to connect (%d)\n", WSAGetLastError()); INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); - closesocket(nsocket); } else { TRACE("Connected to server\n"); lpwfs->sndSocket = nsocket; - INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, - server_addr->addr_str, strlen(server_addr->addr_str)+1); - sock_namelen = sizeof(lpwfs->socketAddress); getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen); diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c index ffb75b8b8f7..a53f83bdd68 100644 --- a/dlls/wininet/http.c +++ b/dlls/wininet/http.c @@ -4960,12 +4960,7 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) request->proxy ? debugstr_w(request->proxy->name) : "(null)"); server = request->proxy ? request->proxy : request->server; assert(server->addr); - INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, - INTERNET_STATUS_CONNECTING_TO_SERVER, - server->addr->addr_str, - strlen(server->addr->addr_str)+1); - - res = create_netconn(server, request->security_flags, + res = create_netconn(server, &request->hdr, request->security_flags, (request->hdr.ErrorMask & INTERNET_ERROR_MASK_COMBINED_SEC_CERT) != 0, request->hdr.connect_timeout, &netconn); if(res != ERROR_SUCCESS) { @@ -4974,11 +4969,6 @@ static DWORD open_http_connection(http_request_t *request, BOOL *reusing) } request->netconn = netconn; - - INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, - INTERNET_STATUS_CONNECTED_TO_SERVER, - server->addr->addr_str, strlen(server->addr->addr_str)+1); - *reusing = FALSE; TRACE("Created connection to %s: %p\n", debugstr_w(request->server->name), netconn); return ERROR_SUCCESS; diff --git a/dlls/wininet/internet.c b/dlls/wininet/internet.c index 2fc1cbc08b9..7eb9dd710cb 100644 --- a/dlls/wininet/internet.c +++ b/dlls/wininet/internet.c @@ -4189,11 +4189,10 @@ BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwRe if(!addr) goto End; init_winsock(); - fd = socket(addr->addr.ss_family, SOCK_STREAM, 0); + fd = create_connect_socket(addr, AF_UNSPEC, INFINITE, NULL, 0); if (fd != -1) { - if (connect(fd, (struct sockaddr *)&addr->addr, addr->addr_len) == 0) - rc = TRUE; + rc = TRUE; closesocket(fd); } free(addr); diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h index 31b447ab638..14c946c7799 100644 --- a/dlls/wininet/internet.h +++ b/dlls/wininet/internet.h @@ -385,6 +385,7 @@ DWORD HTTP_Connect(appinfo_t*,LPCWSTR, DWORD dwInternalFlags, HINTERNET*); server_addr_t *GetAddress(const WCHAR*,INTERNET_PORT); +int create_connect_socket(server_addr_t*,int,DWORD,object_header_t*,DWORD_PTR); DWORD get_cookie_header(const WCHAR*,const WCHAR*,WCHAR**); DWORD set_cookie(substr_t,substr_t,substr_t,substr_t,DWORD); @@ -399,7 +400,7 @@ VOID INTERNET_SendCallback(object_header_t *hdr, DWORD_PTR dwContext, DWORD dwStatusInfoLength); WCHAR *INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto); -DWORD create_netconn(server_t*,DWORD,BOOL,DWORD,netconn_t**); +DWORD create_netconn(server_t*,object_header_t*,DWORD,BOOL,DWORD,netconn_t**); void free_netconn(netconn_t*); void NETCON_unload(void); DWORD NETCON_secure_connect(netconn_t*,server_t*); diff --git a/dlls/wininet/netconnection.c b/dlls/wininet/netconnection.c index 5cf37e0d08f..5741cc412be 100644 --- a/dlls/wininet/netconnection.c +++ b/dlls/wininet/netconnection.c @@ -286,58 +286,26 @@ static void set_socket_blocking(netconn_t *conn, BOOL is_blocking) conn->is_blocking = is_blocking; } -static DWORD create_netconn_socket(server_t *server, netconn_t *netconn, DWORD timeout) +static DWORD create_netconn_socket(server_t *server, object_header_t *hdr, DWORD_PTR callback_context, + netconn_t *netconn, DWORD timeout) { - int result; ULONG flag; - DWORD res; init_winsock(); assert(server->addr); - result = netconn->socket = socket(server->addr->addr.ss_family, SOCK_STREAM, 0); - if(result != -1) { - set_socket_blocking(netconn, FALSE); - result = connect(netconn->socket, (struct sockaddr*)&server->addr->addr, server->addr->addr_len); - if(result == -1) - { - res = WSAGetLastError(); - if (res == WSAEINPROGRESS || res == WSAEWOULDBLOCK) { - FD_SET set; - int res; - socklen_t len = sizeof(res); - TIMEVAL timeout_timeval = {0, timeout*1000}; - - FD_ZERO(&set); - FD_SET(netconn->socket, &set); - res = select(netconn->socket+1, NULL, &set, NULL, &timeout_timeval); - if(!res || res == SOCKET_ERROR) { - closesocket(netconn->socket); - netconn->socket = -1; - return ERROR_INTERNET_CANNOT_CONNECT; - } - if (!getsockopt(netconn->socket, SOL_SOCKET, SO_ERROR, (void *)&res, &len) && !res) - result = 0; - } - } - if(result == -1) - { - closesocket(netconn->socket); - netconn->socket = -1; - } - } - if(result == -1) + if ((netconn->socket = create_connect_socket(server->addr, AF_UNSPEC, timeout, hdr, callback_context)) == -1) return ERROR_INTERNET_CANNOT_CONNECT; flag = 1; - result = setsockopt(netconn->socket, IPPROTO_TCP, TCP_NODELAY, (void*)&flag, sizeof(flag)); - if(result < 0) + if(setsockopt(netconn->socket, IPPROTO_TCP, TCP_NODELAY, (void*)&flag, sizeof(flag)) < 0) WARN("setsockopt(TCP_NODELAY) failed\n"); return ERROR_SUCCESS; } -DWORD create_netconn(server_t *server, DWORD security_flags, BOOL mask_errors, DWORD timeout, netconn_t **ret) +DWORD create_netconn(server_t *server, object_header_t *hdr, DWORD security_flags, + BOOL mask_errors, DWORD timeout, netconn_t **ret) { netconn_t *netconn; int result; @@ -352,7 +320,7 @@ DWORD create_netconn(server_t *server, DWORD security_flags, BOOL mask_errors, D list_init(&netconn->pool_entry); SecInvalidateHandle(&netconn->ssl_ctx); - result = create_netconn_socket(server, netconn, timeout); + result = create_netconn_socket(server, hdr, hdr->dwContext, netconn, timeout); if (result != ERROR_SUCCESS) { free(netconn); return result; @@ -618,7 +586,7 @@ DWORD NETCON_secure_connect(netconn_t *connection, server_t *server) if (res == ERROR_INTERNET_SECURITY_CHANNEL_ERROR && have_compat_cred_handle) { closesocket(connection->socket); - res = create_netconn_socket(connection->server, connection, 500); + res = create_netconn_socket(connection->server, NULL, 0, connection, 500); if (res != ERROR_SUCCESS) return res; res = netcon_secure_connect_setup(connection, TRUE); diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c index 825821663f6..8b6ff6aacbd 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -92,6 +92,58 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) return server_addr; } +int create_connect_socket(server_addr_t *addr, int af, DWORD timeout, object_header_t *hdr, DWORD_PTR callback_context) +{ + TIMEVAL timeout_timeval = {0, timeout * 1000}; + ULONG blocking; + socklen_t len; + FD_SET set; + DWORD err; + int res; + int s; + + if (hdr) + INTERNET_SendCallback(hdr, callback_context, INTERNET_STATUS_CONNECTING_TO_SERVER, + addr->addr_str, strlen(addr->addr_str) + 1); + + if (af != AF_UNSPEC && addr->addr.ss_family != af) + return -1; + + if ((s = socket(addr->addr.ss_family, SOCK_STREAM, 0)) == -1) + return -1; + + blocking = 0; + ioctlsocket(s, FIONBIO, &blocking); + + if (!connect(s, (struct sockaddr *)&addr->addr, addr->addr_len)) + goto done; + + err = WSAGetLastError(); + if (err != WSAEINPROGRESS && err != WSAEWOULDBLOCK) + { + closesocket(s); + return -1; + } + + FD_ZERO(&set); + FD_SET(s, &set); + res = select(s + 1, NULL, &set, NULL, timeout == INFINITE ? NULL : &timeout_timeval); + len = sizeof(res); + if(!res || res == SOCKET_ERROR || getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&res, &len) || res) + { + closesocket(s); + return -1; + } + +done: + blocking = 1; + ioctlsocket(s, FIONBIO, &blocking); + if (hdr) + INTERNET_SendCallback(hdr, callback_context, INTERNET_STATUS_CONNECTED_TO_SERVER, + addr->addr_str, strlen(addr->addr_str) + 1); + return s; +} + /* * Helper function for sending async Callbacks */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/internet.h | 1 + dlls/wininet/utility.c | 95 +++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h index 14c946c7799..89eb86b8ee4 100644 --- a/dlls/wininet/internet.h +++ b/dlls/wininet/internet.h @@ -36,6 +36,7 @@ typedef struct server_addr struct sockaddr_storage addr; int addr_len; char addr_str[INET6_ADDRSTRLEN]; + struct server_addr *next; } server_addr_t; typedef struct { diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c index 8b6ff6aacbd..0c89da29468 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -41,10 +41,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet); server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) { + server_addr_t *server_addr, *p; struct sockaddr_storage *addr; - server_addr_t *server_addr; - ADDRINFOW *res, hints; - unsigned int len; + ADDRINFOW *res, *ai, hints; + unsigned int len, count; int ret; TRACE("%s\n", debugstr_w(name)); @@ -54,7 +54,7 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) * their IPv6 addresses even though they have IPv6 addresses in the DNS. */ hints.ai_family = AF_INET; - + hints.ai_socktype = SOCK_STREAM; ret = GetAddrInfoW(name, NULL, &hints, &res); if (ret != 0) { @@ -67,32 +67,44 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) TRACE("failed to get address of %s\n", debugstr_w(name)); return NULL; } - server_addr = malloc(sizeof(*server_addr)); - addr = &server_addr->addr; - server_addr->addr_len = res->ai_addrlen; - memcpy( addr, res->ai_addr, res->ai_addrlen ); - /* Copy port */ - switch (res->ai_family) + count = 0; + for (ai = res; ai; ai = ai->ai_next) + ++count; + p = server_addr = calloc(count, sizeof(*server_addr)); + ai = res; + while (ai) { - case AF_INET: - ((struct sockaddr_in *)addr)->sin_port = htons(port); - inet_ntop(res->ai_family, &((struct sockaddr_in *)addr)->sin_addr, server_addr->addr_str, INET6_ADDRSTRLEN); - break; - case AF_INET6: - ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); - server_addr->addr_str[0] = '['; - inet_ntop(res->ai_family, &((struct sockaddr_in6 *)addr)->sin6_addr, server_addr->addr_str + 1, INET6_ADDRSTRLEN - 2); - len = strlen(server_addr->addr_str); - server_addr->addr_str[len] = ']'; - server_addr->addr_str[len + 1] = 0; - break; + addr = &p->addr; + p->addr_len = ai->ai_addrlen; + memcpy( addr, ai->ai_addr, ai->ai_addrlen ); + /* Copy port */ + switch (ai->ai_family) + { + case AF_INET: + ((struct sockaddr_in *)addr)->sin_port = htons(port); + inet_ntop(ai->ai_family, &((struct sockaddr_in *)addr)->sin_addr, p->addr_str, INET6_ADDRSTRLEN); + break; + case AF_INET6: + ((struct sockaddr_in6 *)addr)->sin6_port = htons(port); + p->addr_str[0] = '['; + inet_ntop(ai->ai_family, &((struct sockaddr_in6 *)addr)->sin6_addr, p->addr_str + 1, INET6_ADDRSTRLEN - 2); + len = strlen(p->addr_str); + p->addr_str[len] = ']'; + p->addr_str[len + 1] = 0; + break; + } + if (!(ai = ai->ai_next)) + break; + p->next = p + 1; + p = p->next; } FreeAddrInfoW(res); return server_addr; } -int create_connect_socket(server_addr_t *addr, int af, DWORD timeout, object_header_t *hdr, DWORD_PTR callback_context) +static int try_create_connect_socket(server_addr_t *addr, int af, DWORD timeout, object_header_t *hdr, + DWORD_PTR callback_context) { TIMEVAL timeout_timeval = {0, timeout * 1000}; ULONG blocking; @@ -144,6 +156,43 @@ done: return s; } +int create_connect_socket(server_addr_t *addr, int af, DWORD timeout, object_header_t *hdr, DWORD_PTR callback_context) +{ + LARGE_INTEGER qpf, qpc, end; + server_addr_t *a, tmp; + int s; + + if (timeout != INFINITE) + { + QueryPerformanceFrequency(&qpf); + QueryPerformanceCounter(&end); + end.QuadPart += qpf.QuadPart / 1000 * timeout; + } + a = addr; + while (a) + { + if ((s = try_create_connect_socket(a, af, timeout, hdr, callback_context)) != -1) + { + /* try this address first next time. */ + tmp = *addr; + *addr = *a; + *a = tmp; + a->next = addr->next; + addr->next = tmp.next; + return s; + } + if (timeout != INFINITE) + { + QueryPerformanceCounter(&qpc); + if (qpc.QuadPart >= end.QuadPart) + return -1; + timeout = (end.QuadPart - qpc.QuadPart) * 1000 / qpf.QuadPart; + } + a = a->next; + } + return -1; +} + /* * Helper function for sending async Callbacks */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/wininet/tests/http.c | 8 ++++---- dlls/wininet/utility.c | 10 ---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/dlls/wininet/tests/http.c b/dlls/wininet/tests/http.c index a7a0a6bb75a..a2af733497d 100644 --- a/dlls/wininet/tests/http.c +++ b/dlls/wininet/tests/http.c @@ -6888,11 +6888,11 @@ static void test_af_priority(int port) ok(data.resolving && !strcmp(data.resolving, "localhost"), "got %s.\n", debugstr_a(data.resolving)); free(data.resolving); ok(data.resolved_count == 1, "got %d.\n", data.resolved_count); - todo_wine ok(data.resolved && !strcmp(data.resolved, "[::1]"), "got %s.\n", debugstr_a(data.resolved)); + ok(data.resolved && !strcmp(data.resolved, "[::1]"), "got %s.\n", debugstr_a(data.resolved)); free(data.resolved); - todo_wine ok(data.connecting_count == 2, "got %d.\n", data.connecting_count); - todo_wine ok(data.connecting[0] && !strcmp(data.connecting[0], "[::1]"), "got %s.\n", debugstr_a(data.connecting[0])); - todo_wine ok(data.connecting[1] && !strcmp(data.connecting[1], "127.0.0.1"), "got %s.\n", debugstr_a(data.connecting[1])); + ok(data.connecting_count == 2, "got %d.\n", data.connecting_count); + ok(data.connecting[0] && !strcmp(data.connecting[0], "[::1]"), "got %s.\n", debugstr_a(data.connecting[0])); + ok(data.connecting[1] && !strcmp(data.connecting[1], "127.0.0.1"), "got %s.\n", debugstr_a(data.connecting[1])); for (i = 0; i < ARRAY_SIZE(data.connecting); ++i) free(data.connecting[i]); ok(data.connected_count == 1, "got %d.\n", data.connected_count); diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c index 0c89da29468..666843d2220 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -50,19 +50,9 @@ server_addr_t *GetAddress(const WCHAR *name, INTERNET_PORT port) TRACE("%s\n", debugstr_w(name)); memset( &hints, 0, sizeof(hints) ); - /* Prefer IPv4 to IPv6 addresses, since some servers do not listen on - * their IPv6 addresses even though they have IPv6 addresses in the DNS. - */ - hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; ret = GetAddrInfoW(name, NULL, &hints, &res); if (ret != 0) - { - TRACE("failed to get IPv4 address of %s, retrying with IPv6\n", debugstr_w(name)); - hints.ai_family = AF_INET6; - ret = GetAddrInfoW(name, NULL, &hints, &res); - } - if (ret != 0) { TRACE("failed to get address of %s\n", debugstr_w(name)); return NULL; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
v2: * return server_addr_t from GetAddress(); * dont't pass callback_context to create_netconn(). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10794#note_138624
participants (2)
-
Paul Gofman -
Paul Gofman (@gofman)