Search code examples
c++windowswinapiwinhttppac

The data obtained by WinHttpGetProxyForUrl is always null


I have the following code:

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0A00
#endif

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <windows.h>
#include <winhttp.h>
#include <string>
#include <iostream>

#pragma comment(lib, "winhttp.lib")

int wmain(int argc, wchar_t* argv[]) {
    if (argc != 2) {
        std::wcerr << L"Parameters: <Domain>" << std::endl;
        return 1;
    }

    // Get proxy settings
    WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ProxyConfig;
    ZeroMemory(&ProxyConfig, sizeof(ProxyConfig));
    if (!WinHttpGetIEProxyConfigForCurrentUser(&ProxyConfig)) {
        std::wcerr << L"WinHttpGetIEProxyConfigForCurrentUser failed, error: "
            << GetLastError() << std::endl;
        return 1;
    }

    if (ProxyConfig.lpszProxy != NULL) {
        // Check bypass list
        if (ProxyConfig.lpszProxyBypass != NULL) {
            wchar_t* domain = wcstok(ProxyConfig.lpszProxyBypass, L";");

            while (domain != NULL) {
                if (wcscmp(domain, argv[1]) == 0) {
                    std::wcout << L"Proxy Bypass" << std::endl;
                    GlobalFree(ProxyConfig.lpszProxy);
                    GlobalFree(ProxyConfig.lpszProxyBypass);
                    return 0;
                }

                domain = wcstok(NULL, L";");
            }

            GlobalFree(ProxyConfig.lpszProxyBypass);
        }

        std::wcout << L"Proxy: " << ProxyConfig.lpszProxy << std::endl;
        GlobalFree(ProxyConfig.lpszProxy);
        return 0;
    }

    // Set AUTOPROXY options
    WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions;
    ZeroMemory(&AutoProxyOptions, sizeof(AutoProxyOptions));
    if (ProxyConfig.fAutoDetect) {
        AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
        AutoProxyOptions.dwAutoDetectFlags =
            WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
    }
    if (ProxyConfig.lpszAutoConfigUrl != NULL) {
        AutoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
        AutoProxyOptions.lpszAutoConfigUrl = ProxyConfig.lpszAutoConfigUrl;

    }
    if (AutoProxyOptions.dwFlags == NULL) {
        std::wcout << L"No Proxy" << std::endl;
        return 0;
    }
    AutoProxyOptions.fAutoLogonIfChallenged = TRUE;

    // Create WinHTTP session
    HINTERNET hSession = WinHttpOpen(
        L"Windows",
        WINHTTP_ACCESS_TYPE_NO_PROXY,
        WINHTTP_NO_PROXY_NAME,
        WINHTTP_NO_PROXY_BYPASS,
        0
    );
    if (hSession == NULL) {
        std::wcerr << L"WinHttpOpen failed, error: "
            << GetLastError() << std::endl;
        return 1;
    }

    // Get proxy info from PAC
    std::wstring wurl = L"http://" + (std::wstring)argv[1] + L"/";
    WINHTTP_PROXY_INFO ProxyInfo;
    ZeroMemory(&ProxyInfo, sizeof(ProxyInfo));
    if (!WinHttpGetProxyForUrl(
        hSession,
        wurl.c_str(),
        &AutoProxyOptions,
        &ProxyInfo
    )) {
        std::wcerr << L"WinHttpGetProxyForUrl failed, error: "
            << GetLastError() << std::endl;
        WinHttpCloseHandle(hSession);
        return 1;
    }

    // ProxyInfo.lpszProxy contains the address of the proxy server
    if (ProxyInfo.lpszProxy != NULL) {
        std::wcout << L"Proxy: " << ProxyInfo.lpszProxy << std::endl;
        GlobalFree(ProxyInfo.lpszProxy);
    } else {
        std::wcout << L"Proxy: NULL" << std::endl;
    }

    // Cleanup
    if (ProxyConfig.lpszAutoConfigUrl != NULL) {
        GlobalFree(ProxyConfig.lpszAutoConfigUrl);
    }

    WinHttpCloseHandle(hSession);

    return 0;
}

It uses the WinHttpGetProxyForUrl function to retrieve the proxy server address specified in the PAC file based on system settings.

The WinHttpGetProxyForUrl function executed successfully without returning an error, but the given ProxyInfo.lpszProxy variable value is always null.

The following is the content of the original PAC file I used:

var pHost = "Proxy.local";
var pPort = "1080";
var Socks5 = "SOCKS5 " + pHost + ":" + pPort + "; ";
var Socks = "SOCKS " + pHost + ":" + pPort + "; ";
var Direct = "DIRECT; ";

function FindProxyForURL(url, host) {
        if (shExpMatch(dnsResolveEx(host), "*.*.*.*")) {
                return Socks5 + Socks + Direct;
        } else {
                return Direct;
        }
}

The following is a simplified version of the PAC file that always returns a proxy server:

function FindProxyForURL(url, host) {
    return "SOCKS Proxy.local:1080";
}

I have tried both, but the ProxyInfo.lpszProxy variable still gets null.

Is there an issue with my code or is the WinHttpGetProxyForUrl function exhibiting unexpected behavior?


Solution

  • The reason for the error result is that the WinHttpGetProxyForUrl function only recognizes "PROXY" and does not recognize "SOCKS" or "SOCKS5". If you observe the content of the PAC file I mentioned in the question, you will find that I am using either "SOCKS" or "SOCKS5" instead of "PROXY", and these two are ignored by the WinHttpGetProxyForUrl function, which is why I always got NULL.

    This is an undocumented and unexpected behavior. For the unexpected behavior I mentioned, on the one hand, it refers to the behavior that was not described in the document, and on the other hand, I believe that ignoring "SOCKS" and "SOCKS5" does not match the results I expected after the function is executed.

    Anyway, thank him @Torrecto-MSFT for inspiring me to find the real problem.