HttpSendRequest fails with error 12015

2019-08-22 06:40发布

问题:

I have created a Win32 Service which uses WinInet to send HTTP-requests to remote host. On my computer (WinXP SP2), on test workstations in our QoS-team (Win2003 Server) it works fine - over proxy and direct, proxy with auth and without).

But some of our customers, that uses this service and_proxy_with_authorization on Win2003 Server, have a problem - all calls of HttpSendRequest fails and GetLastError returns 12015 (ERROR_INTERNET_LOGIN_FAILURE, The request to connect and log on to an FTP server failed). Herewith, equal HTTP-request, manually sent from IE address-line, succeeded.

Proxy configuration seems to be correct. Here is initialization code:

m_hNet = InternetOpen(m_strAgent.c_str(),
            INTERNET_OPEN_TYPE_PRECONFIG,
            NULL,
            NULL,
            0);

        // Respect explicit proxy
        if (Cfg::m_bUseProxy)
        {
            char szProxy[MAX_PATH] = {0};
            strncpy(szProxy, m_strProxyServer.c_str(), MAX_PATH - 1);

            INTERNET_PROXY_INFO proxyinfo;
            proxyinfo.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
            proxyinfo.lpszProxy = szProxy;
            proxyinfo.lpszProxyBypass = NULL;

            BOOL B = InternetSetOption(m_hNet, INTERNET_OPTION_PROXY, (LPVOID)(&proxyinfo), sizeof(proxyinfo));
            if (!B)
            {
                devent(TS::LL_HIGH, "[Wrn] InternetSetOption::Proxy failed <proxy=%s><le=%d>",
                    m_strProxyServer.c_str(),
                    GetLastError());
            }
        }


        // Validate handle
        if (NULL == m_hNet)
        {
            devent(TS::LL_CRITICAL, "[Err] InternetOpen failed <le=%d>",
                GetLastError());
            return false;
        }


        // Try to get connection handle
        m_hConnect = InternetConnect(m_hNet,
            m_strHostName.c_str(),
            INTERNET_DEFAULT_HTTP_PORT,
            NULL,
            NULL,
            INTERNET_SERVICE_HTTP,
            0,
            0);


        // Validate handle
        if (NULL == m_hConnect)
        {
            devent(TS::LL_CRITICAL, "[Err] InternetConnect failed <le=%d>",
                GetLastError());
            Cleanup();
            return false;
        }

        // Respect proxy authentication
        if (Cfg::m_bUseAuth)
        {
            BOOL B;
            B = InternetSetOption(m_hConnect, INTERNET_OPTION_PROXY_USERNAME, (LPVOID*)Cfg::m_strProxyLogin.c_str(), Cfg::m_strProxyLogin.length() + 1);
            if (!B)
            {
                devent(TS::LL_HIGH, "[Wrn] InternetSetOption::ProxyUserName failed <login=%s><le=%d>",
                    Cfg::m_strProxyLogin.c_str(),
                    GetLastError());
            }

            B = InternetSetOption(m_hConnect, INTERNET_OPTION_PROXY_PASSWORD, (LPVOID*)Cfg::m_strProxyPassword.c_str(), Cfg::m_strProxyPassword.length() + 1);
            if (!B)
            {
                devent(TS::LL_HIGH, "[Wrn] InternetSetOption::ProxyPassword failed <pass=%s><le=%d>",
                    Cfg::m_strProxyPassword.c_str(),
                    GetLastError());
            }
        }

And this is sending:

// Try to get request handle
        m_hRequest = HttpOpenRequest(m_hConnect,
            "POST",
            m_strReqObject.c_str(),
            NULL,
            NULL,
            NULL,
            INTERNET_FLAG_NO_CACHE_WRITE,
            0);

        // Validate handle
        if (NULL == m_hRequest)
        {
            devent(TS::LL_CRITICAL, "[Err] OpenRequest failed <le=%d>",
                GetLastError());
            return false;
        }

        // Try to get response
        BOOL bOk = HttpSendRequest(m_hRequest,
            strSpecificHeaders.c_str(),
            strSpecificHeaders.length(),
            (LPVOID)strRequest.c_str(),
            (DWORD)strRequest.length());

        if (0 == bOk)
        {
            devent(TS::LL_CRITICAL, "[Err] SendRequest failed <le=%d>",
                GetLastError());

            CloseHandle(m_hRequest);
            return false;
        }

I have googled two days, but not found not only solutions but also similar problems by anybody. Also i cannot reproduce problem on similar workstations. And finally, i cant understand why "FTP-Server" in msdn error desc?

Any ideas?

回答1:

WinInet is designed for use by Application space users, and has ties back to Registry locations for the WinInet options (based on logged in user account).

The online MSDN Documentation clearly states on every WinInet page:

Note WinINet does not support server implementations. In addition, it should not be used from a service. For server implementations or services use Microsoft Windows HTTP Services (WinHTTP).

The difference in API between WinInet and WinHTTP are very small, you will pick it up very quickly.

  • Functions renamed (e.g. HttpOpenRequest -> WinHttpOpenRequest)
  • All WinHttp methods are Unicode only
  • uses WinHttp.lib,WinHttp.dll instead of WinInet.lib, WinInet.dll

There are a few other subtleties, but you won't struggle.

I know your question suggests the code works for people already with it running it as a service. But the fact that the documentation specifically says "don't do it" may suggest that some versions of Windows may not support it, specifically the later versions, and that some Service Packs or Automatic Updates may cause the current working instances to fail.

I would hazzard a guess that there may even be some Security Tokens and Privileges that WinInet needs under the hood to complete tasks, and some installs of Windows Server 200x may have the default setup to not provide those tokens to users running as a service account.

I really recommend trying to port a quick test service to WinHTTP and testing on your client's Windows Server implementation to see if that resolves the problem.



回答2:

On a completely different tack... The ERROR_INTERNET_LOGIN_FAILURE can also suggest that a firewall has blocked that connection.

A possible solution was described here at Egg Head Cafe