[PATCH 0/7] MR10794: wininet: Attempt connecting to multiple host addresses and don't forcefully prefer ipv4.
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 | 10 +++++----- dlls/wininet/internet.h | 14 +++++++++----- dlls/wininet/netconnection.c | 6 +++--- dlls/wininet/utility.c | 29 +++++++++++++---------------- 6 files changed, 48 insertions(+), 49 deletions(-) diff --git a/dlls/wininet/ftp.c b/dlls/wininet/ftp.c index f3f586cd6e1..573eb3d09d4 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 = NULL; 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 (!GetAddress(lpszServerName, lpwfs->serverport, &server_addr)) { 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..59a2aa87efe 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 (!GetAddress(server->name, server->port, &server->addr)) 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..538c9ee2652 100644 --- a/dlls/wininet/internet.c +++ b/dlls/wininet/internet.c @@ -4176,8 +4176,7 @@ 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; @@ -4186,18 +4185,19 @@ BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwRe if (!host_z) return FALSE; - b = GetAddress(host_z, port, (struct sockaddr *)&saddr, &sa_len, NULL); + b = GetAddress(host_z, port, &addr); free(host_z); if(!b) 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..4803bfde433 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*); +BOOL GetAddress(const WCHAR*,INTERNET_PORT,server_addr_t**); 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..daeb77538a8 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -39,14 +39,16 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet); -BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, struct sockaddr *psa, int *sa_len, char *addr_str) +BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_addr) { + struct sockaddr_storage *addr; ADDRINFOW *res, hints; - void *addr = NULL; + void *ip_addr = NULL; int ret; TRACE("%s\n", debugstr_w(name)); + *server_addr = NULL; 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. @@ -65,29 +67,24 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, struct sockaddr *psa, int TRACE("failed to get address of %s\n", debugstr_w(name)); return FALSE; } - 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; } -- 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 daeb77538a8..48a2fa7d284 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -43,7 +43,7 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad { struct sockaddr_storage *addr; ADDRINFOW *res, hints; - void *ip_addr = NULL; + unsigned int len; int ret; TRACE("%s\n", debugstr_w(name)); @@ -75,16 +75,19 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad 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 TRUE; } -- 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 573eb3d09d4..7ae832efe0b 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 59a2aa87efe..fa7219211f7 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->hdr.dwContext, 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 538c9ee2652..e7874996215 100644 --- a/dlls/wininet/internet.c +++ b/dlls/wininet/internet.c @@ -4190,11 +4190,10 @@ BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwRe if(!b) 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 4803bfde433..45c6ab532d2 100644 --- a/dlls/wininet/internet.h +++ b/dlls/wininet/internet.h @@ -385,6 +385,7 @@ DWORD HTTP_Connect(appinfo_t*,LPCWSTR, DWORD dwInternalFlags, HINTERNET*); BOOL GetAddress(const WCHAR*,INTERNET_PORT,server_addr_t**); +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_PTR,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..b2ac91848f6 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_PTR callback_context, 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, callback_context, 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 48a2fa7d284..a319754b835 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -92,6 +92,58 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad return TRUE; } +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 | 94 +++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h index 45c6ab532d2..54828baa53b 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 a319754b835..480f7e5b630 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -42,8 +42,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet); BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_addr) { struct sockaddr_storage *addr; - ADDRINFOW *res, hints; - unsigned int len; + ADDRINFOW *res, *ai, hints; + unsigned int len, count; + server_addr_t *p; int ret; TRACE("%s\n", debugstr_w(name)); @@ -54,7 +55,7 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad * 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 +68,44 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad TRACE("failed to get address of %s\n", debugstr_w(name)); return FALSE; } - *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 TRUE; } -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 +157,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 480f7e5b630..aaac35dda9b 100644 --- a/dlls/wininet/utility.c +++ b/dlls/wininet/utility.c @@ -51,19 +51,9 @@ BOOL GetAddress(const WCHAR *name, INTERNET_PORT port, server_addr_t **server_ad *server_addr = NULL; 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 FALSE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10794
Fixes Le Mans Ultimate. The game binds some internal web server to '[localhost](http://localhost)' ipv6 address ::1 (that is the first one available from getaddrinfo both on Windows and Wine). Then it tries to make a http query through wininet using 'localhost' host address. The current logic in wininet forces ipv4 address in name resolution if one is available (obviously the case for localhost) and then ipv4 connection to 127.0.0.1 fails. Just not preferring ipv4 would break a lot probably, starting from the opposite case when an app bind local server to 127.0.0.1. But Windows tries other available host addresses if the first one fails (and tries once succeeded address at once on the same request when there is no name resolution step). The weird thing is that INTERNET_STATUS_RESOLVING_NAME always comes just once and has the first host address from the list, while there might be multiple INTERNET_STATUS_CONNECTING_TO_SERVER and finally one INTERNET_STATUS_CONNECTED_TO_SERVER if the connection succeeds (that is covered by the test). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10794#note_138479
WRT the timeout logic in create_connect_socket(), I ad-hoc tested on Windows that setting INTERNET_OPTION_CONNECT_TIMEOUT affect this in a seemingly similar way: if timeout is expired on first address connect attempt it won't attempt the next, as callback calls go. Such a test seems to be a bit hard to incorporate reliably, for some reason on Windows connection to [localhost](http://localhost) ::1 takes considerable time before it fails (moves to 127.0.0.1 attempt) or times out which is not the case on Wine / Linux. Unrelated, in case of timeout Windows returns INTERNET_OPTION_CONNECT_TIMEOUT instead of ERROR_INTERNET_CANNOT_CONNECT (while we always return the latter, both before and after my patches). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10794#note_138480
WRT FTP, as far as my ad-hoc testing went the ipv6 connection preference logic is the same. However, enabling it for FTP requires more work (handling of ipv6 data port address which needs another FTP protocol command), so I left it behind for now (with these patches FTP will still attempt to connect to ipv4 only). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10794#note_138484
participants (2)
-
Paul Gofman -
Paul Gofman (@gofman)