【问题标题】:Trying to get socket from wininet HTTP connection试图从 wininet HTTP 连接获取套接字
【发布时间】:2011-12-24 16:46:23
【问题描述】:

我们有一个旧版应用程序,它通过 Web 代理实现 SSL 隧道。它使用 wininet API,并且在 XP 上运行良好多年,但现在在 Windows 7 上失败了。我尝试隔离代码并制作了一个小程序来重现该问题。这是一个用 MSVC 9 编译的小型 C 程序。见下文。

在 Windows 7 上,一旦连接到代理(状态代码 200),我就无法从 API 获取套接字描述符。我得到的只是一个 INVALID_SOCKET,即使所有 wininet 函数都成功返回并且 GetLastError() 返回 0。

在 XP 机器上,一切正常,返回的套接字是有效的。

有人知道吗? 非常感谢您。

#include <windows.h>
#include <wininet.h>
#include <stdio.h>

const char *_connect()
{
    HINTERNET hOpen = 0;
    HINTERNET hConnect = 0;
    HINTERNET hRequest = 0;
    int remotePort = 443;
    const char *remoteHost = "a.b.c.d"; // Cannot disclose
    hOpen = InternetOpen("wininet-test", INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
    if (!hOpen) return "InternetOpen";
    hConnect = InternetConnect(hOpen, remoteHost, remotePort, 0, 0, INTERNET_SERVICE_HTTP, 0, 0);
    if (!hConnect) return "InternetConnect";
    {
        DWORD flags =
            INTERNET_FLAG_CACHE_IF_NET_FAIL |
            INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
            INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
            INTERNET_FLAG_KEEP_CONNECTION |
            INTERNET_FLAG_NO_CACHE_WRITE |
            INTERNET_FLAG_PRAGMA_NOCACHE |
            INTERNET_FLAG_RELOAD |
            INTERNET_FLAG_RESYNCHRONIZE |
            INTERNET_FLAG_SECURE;
        char url[100];
        sprintf(url, "http://%s:%d/", remoteHost, remotePort);
        hRequest = HttpOpenRequest(hConnect, "GET", "connect.html", "HTTP/1.0", url, 0, flags, 0);
        if (!hRequest) return "HttpOpenRequest";
    }
    {
        DWORD flags=0;
        DWORD bufferLength = sizeof(flags);
        if (!InternetQueryOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, &bufferLength)) {
            return "InternetQueryOption";
        }
        flags |= (SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_REVOCATION);
        if (!InternetSetOption(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &flags, sizeof(flags))) {
            return "InternetSetOption";
        }
    }
    if (!HttpSendRequest(hRequest, 0, 0, 0, 0)) {
        return "HttpSendRequest";
    } else {
        char buffer[4];
        DWORD bufferSize = sizeof(buffer);
        if (!HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, &buffer, &bufferSize, NULL)) {
        return "HttpQueryInfo";
        } else if (atoi(buffer) != 200) {
            return "status code";
        }
    }
    {
        INTERNET_DIAGNOSTIC_SOCKET_INFO idsi;
        DWORD bufferSize = sizeof(idsi);
        if (!InternetQueryOption(hRequest, INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO, &idsi, &bufferSize)) {
            return "InternetQueryOption";
        } else if (idsi.Socket == INVALID_SOCKET) {
            /* This is always the case on our Windows 7 platform, why? */
            return "invalid socket";
        }
    }
    return 0;
}

int main(int argc, const char **argv)
{
    const char *error = _connect();
    if (error) {
        printf("ERROR: %s (%d)\n", error, GetLastError());
    } else {
        printf("SUCCESS\n");
    }
    return 0;
}

【问题讨论】:

    标签: c sockets windows-7 wininet


    【解决方案1】:

    From MSDN

    INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO 67

    检索包含有关指定 HTTP 请求的数据的 INTERNET_DIAGNOSTIC_SOCKET_INFO 结构。 InternetQueryOption 使用此标志。

    Windows 7:不再支持此选项。

    【讨论】:

      【解决方案2】:

      HTTP 请求是否使用 keep-alives?如果不是,那么我的猜测是Win7下的WinInet在收到服务器响应时关闭它后使套接字句柄失效,而XP没有使套接字失效。

      【讨论】:

      • 标志 INTERNET_FLAG_KEEP_CONNECTION 已设置,并且使用嗅探器,我得到相同的 HTTP 请求标头,其中我有“代理连接:保持活动”。但是,在 XP 上,TCP 连接保持活动状态,但在 7 上,客户端在 SSL 握手完成时发送一个 FIN。所以你是对的,我得到一个 INVALID_SOCKET 因为 TCP 连接被终止,但我不明白为什么它在 7 上是这样,它仍然在 XP 上保持连接。
      • 请求使用的是 HTTP 1.0 还是 1.1?如果使用 HTTP 1.0,连接的非代理部分是否有 Connection: keep-alive 标头?响应的Proxy-ConnectionConnection 标头是否也显示keep-alive
      • 请求使用 HTTP 1.0。我无法检查 Connection: keep-alive 是否已发送,因为当我查看 Wireshark 日志时,它似乎已被 SSL 层加密。代理响应是HTTP/1.0 200 Connection established,没有发回标头。无论客户端是在 XP 还是 7 上运行,HTTP 消息都是相同的。但是在 7 上,在代理响应(状态 200)之后,在加密握手之后,我的客户端发送一些应用程序 SSLv3 加密数据(GET 请求?),然后它从服务器接收一些应用程序数据,然后关闭连接(客户端发送的 TCP FIN)。
      • 这种行为确实表明Connection: keep-alive 没有在请求中发送或Connection: close 正在响应中发送。您正在使用 WinInet,因此您可以使用 Fiddler (fiddler2.com) 查看 SSL 加密的 HTTP 流量,因为它会自动将自身安装为 WinInet 代理,因此它会在 SSL 加密之前查看出站数据,并在 SSL 解密后查看入站数据.
      • 好吧,我终于搞定了。问题是一旦收到服务器响应,wininet 就会将连接放回池中。因此,当我请求它时,套接字不再与会话相关联。所以,我最终使用 HttpSendRequestEx() 让 wininet 相信我还没有完成发送请求。这样wininet保持与我的会话相关联的连接,当我得到套接字时,我自己解析响应并继续。无论如何,感谢您的提示和时间。非常感谢。
      【解决方案3】:

      您确定您的项目不是为 unicode 编译的吗?如果确实需要将char 声明更改为wchar_t 并在常量前面加上L,例如:

      const wchar_t * pszTmp = L"hello world";
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-17
        • 1970-01-01
        • 2010-12-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-18
        • 2012-02-05
        • 2013-04-27
        相关资源
        最近更新 更多