Autoproxy Support in WinINet

The Windows Internet (WinINet) application programming interface (API) provides limited autoproxy support. However, because WinINet should not be used in a service, it is better to use Windows HTTP Services (WinHTTP) version 5.1 instead. For more information, see WinHTTP AutoProxy Support.

The following sections describe how to use WinINet autoproxy support:

Overview of WinINet Autoproxy Support

WinINet autoproxy support relies on four functions:

Calling WinINet Autoproxy Functions using Dynamic Linking

The three functions mentioned above whose name begins with "Internet" reside in "JSProxy.dll", and must be linked to dynamically at runtime. To do so, use the corresponding function-pointer type definitions in "WinINet.h", together with LoadLibrary and GetProcAddress, as illustrated in the following code snippet:

// Declare an HMODULE handle
  HMODULE hModJS;

// Declare function pointers for the three autoproxy functions
  pfnInternetInitializeAutoProxyDll   pInternetInitializeAutoProxyDll;
  pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
  pfnInternetGetProxyInfo             pInternetGetProxyInfo;

// Using LoadLibrary, load "jsproxy.dll"
  hModJS = LoadLibrary( "jsproxy.dll" );

// Using the HMODULE handle to jsproxy.dll provided by LoadLibrary,
// now call GetProcAddress to initialize the function pointers

  pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)
                GetProcAddress( hModJS, 
                                "InternetInitializeAutoProxyDll" );

  pInternetDeInitializeAutoProxyDll = 
                (pfnInternetDeInitializeAutoProxyDll)
                 GetProcAddress(hModJS, 
                                "InternetDeInitializeAutoProxyDll" );

  pInternetGetProxyInfo = (pfnInternetGetProxyInfo)
                GetProcAddress( hModJS, "InternetGetProxyInfo" );

Providing Proxy Auto-Config (PAC) Helper Functions

It is possible to install helper functions as defined in the Navigator Proxy Auto-Config (PAC) File Formatspecification. These functions are then available to your auto-config script. WinINet supports the installation of four such functions, as documented in the AutoProxyHelperVtbl structure:

  • IsResolvable
  • GetIPAddress (see myIPAddress in the Navigator document)
  • ResolveHostName (see dnsResolve in the Navigator document)
  • IsInNet

After providing implementations of these helper functions, you can install them when you initialize WinINet autoproxy support.

Using the WinINet Autoproxy Functions

The WinINet autoproxy functions are used in the following order:

  1. DetectAutoProxyUrl is called to identify a location from which to obtain the auto-config script.
  2. InternetInitializeAutoProxyDll is called to install the script and optionally helper functions.
  3. InternetGetProxyInfo is called to obtain proxy information.
  4. InternetDeInitializeAutoProxyDll is called to release resources associated with the autoproxy script processing.

The following code snippet illustrates this progression:

// The first step is to find the WPAD location using DetectAutoProxyUrl
   DetectAutoProxyUrl( WPADLocation,
                       sizeof(WPADLocation), 
                       PROXY_AUTO_DETECT_TYPE_DHCP | 
                       PROXY_AUTO_DETECT_TYPE_DNS_A );

// Next, download the script to a file of a specified name
   URLDownloadToFileA( NULL, WPADLocation, pFileName, 0, NULL );

   // Set up autoproxy using this local file and the 
   // helper-function v-table
   pInternetInitializeAutoProxyDll( 0, 
                                    pFileName, 
                                    NULL, 
                                    &HelperFunctions, 
                                    NULL );

// Obtain proxy information
   pInternetGetProxyInfo( (LPSTR) url,  strlen( url ), 
                          (LPSTR) host, strlen( host ),
                          &proxy, &dwProxyHostNameLength );

// Release resources associated with the autoproxy functionality
   pInternetDeInitializeAutoProxyDll( NULL, 0 );

A Complete WinINet Autoproxy Example Program

The code below creates a Windows console application that locates and displays proxy information. It illustrates how to use WinINet autoproxy functions. To build it successfully, you need to install the latest version of the Windows SDK.

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <winsock2.h>
#include <ws2tcpip.h>
#include <wininet.h>
#include <urlmon.h>
#include <stdio.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "urlmon.lib")

/* ==================================================================
                            HELPER FUNCTIONS
   ================================================================== */

/////////////////////////////////////////////////////////////////////
//  ResolveHostName                               (a helper function)
/////////////////////////////////////////////////////////////////////
DWORD __stdcall ResolveHostName(LPSTR lpszHostName,
                                LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
{
    DWORD dwIPAddressSize;
    struct addrinfo Hints;
    LPADDRINFO lpAddrInfo;
    LPADDRINFO IPv4Only;
    DWORD error;

    // Figure out first whether to resolve a name or an address literal.
    // If getaddrinfo( ) with the AI_NUMERICHOST flag succeeds, then
    // lpszHostName points to a string representation of an IPv4 or IPv6 
    // address. Otherwise, getaddrinfo( ) should return EAI_NONAME.
    ZeroMemory(&Hints, sizeof (struct addrinfo));
    Hints.ai_flags = AI_NUMERICHOST;    // Only check for address literals.
    Hints.ai_family = PF_UNSPEC;        // Accept any protocol family.
    Hints.ai_socktype = SOCK_STREAM;    // Constrain results to stream socket.
    Hints.ai_protocol = IPPROTO_TCP;    // Constrain results to TCP.

    error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
    if (error != EAI_NONAME) {
        if (error != 0) {
            error = (error == EAI_MEMORY) ?
                ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
            goto quit;
        }
        freeaddrinfo(lpAddrInfo);

        // An IP address (either v4 or v6) was passed in, so if there is 
        // room in the lpszIPAddress buffer, copy it back out and return.
        dwIPAddressSize = lstrlen(lpszHostName);

        if ((*lpdwIPAddressSize < dwIPAddressSize) || (lpszIPAddress == NULL)) {
            *lpdwIPAddressSize = dwIPAddressSize + 1;
            error = ERROR_INSUFFICIENT_BUFFER;
            goto quit;
        }
        strcpy_s(lpszIPAddress, *lpdwIPAddressSize, lpszHostName);
        goto quit;
    }
    // Call getaddrinfo( ) again, this time with no flag set.
    Hints.ai_flags = 0;
    error = getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo);
    if (error != 0) {
        error = (error == EAI_MEMORY) ?
            ERROR_NOT_ENOUGH_MEMORY : ERROR_INTERNET_NAME_NOT_RESOLVED;
        goto quit;
    }
    // Convert the IP address in addrinfo into a string.
    // (the following code only handles IPv4 addresses)
    IPv4Only = lpAddrInfo;
    while (IPv4Only->ai_family != AF_INET) {
        IPv4Only = IPv4Only->ai_next;
        if (IPv4Only == NULL) {
            error = ERROR_INTERNET_NAME_NOT_RESOLVED;
            goto quit;
        }
    }
    error = getnameinfo(IPv4Only->ai_addr,
                        IPv4Only->ai_addrlen,
                        lpszIPAddress,
                        *lpdwIPAddressSize, NULL, 0, NI_NUMERICHOST);
    if (error != 0)
        error = ERROR_INTERNET_NAME_NOT_RESOLVED;

  quit:
    return (error);
}

/////////////////////////////////////////////////////////////////////
//  IsResolvable                                  (a helper function)
/////////////////////////////////////////////////////////////////////
BOOL __stdcall IsResolvable(LPSTR lpszHost)
{
    char szDummy[255];
    DWORD dwDummySize = sizeof (szDummy) - 1;

    if (ResolveHostName(lpszHost, szDummy, &dwDummySize))
        return (FALSE);
    return TRUE;
}

/////////////////////////////////////////////////////////////////////
//  GetIPAddress                                  (a helper function)
/////////////////////////////////////////////////////////////////////
DWORD __stdcall GetIPAddress(LPSTR lpszIPAddress, LPDWORD lpdwIPAddressSize)
{
    char szHostBuffer[255];

    if (gethostname(szHostBuffer, sizeof (szHostBuffer) - 1) != ERROR_SUCCESS)
        return (ERROR_INTERNET_INTERNAL_ERROR);
    return (ResolveHostName(szHostBuffer, lpszIPAddress, lpdwIPAddressSize));
}

/////////////////////////////////////////////////////////////////////
//  IsInNet                                       (a helper function)
/////////////////////////////////////////////////////////////////////
BOOL __stdcall IsInNet(LPSTR lpszIPAddress, LPSTR lpszDest, LPSTR lpszMask)
{
    DWORD dwDest;
    DWORD dwIpAddr;
    DWORD dwMask;

    dwIpAddr = inet_addr(lpszIPAddress);
    dwDest = inet_addr(lpszDest);
    dwMask = inet_addr(lpszMask);

    if ((dwDest == INADDR_NONE) ||
        (dwIpAddr == INADDR_NONE) || ((dwIpAddr & dwMask) != dwDest))
        return (FALSE);

    return (TRUE);
}

/////////////////////////////////////////////////////////////////////
//  reportFuncErr                            (simple error reporting)
/////////////////////////////////////////////////////////////////////
void reportFuncErr(TCHAR * funcName)
{
    printf("\n  ERROR: %s failed with error number %d.\n", funcName,
           GetLastError());
    exit(1);
}

/* ==================================================================
      * * * The  main( ) function of the test application  * * *
   ================================================================== */
int __cdecl main(int argc, char * argv[])
{
    char url[1025] = "http://www.microsoft.com/windows/default.mspx";
    char host[256] = "www.microsoft.com";
    char WPADLocation[1024] = "";
    char TempPath[MAX_PATH];
    char TempFile[MAX_PATH];
    char proxyBuffer[1024];
    char *proxy = proxyBuffer;
    DWORD dwProxyHostNameLength = 1024;
    DWORD returnVal;
    HMODULE hModJS;

    // Declare and populate an AutoProxyHelperVtbl structure, and then 
    // place a pointer to it in a containing AutoProxyHelperFunctions 
    // structure, which will be passed to InternetInitializeAutoProxyDll
    AutoProxyHelperVtbl OurVtbl = {
        IsResolvable,
        GetIPAddress,
        ResolveHostName,
        IsInNet,
        NULL,
        NULL,
        NULL,
        NULL
    };
    
    // Declare function pointers for the three autoproxy functions
    pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
    pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
    pfnInternetGetProxyInfo pInternetGetProxyInfo;

    AutoProxyHelperFunctions HelperFunctions;
     
    HelperFunctions.lpVtbl = &OurVtbl;

    ZeroMemory(proxy, 1024);

    printf("\nWinINet Proxy Test Program output:\n");

    if (!(hModJS = LoadLibrary("jsproxy.dll")))
        reportFuncErr("LoadLibrary");

    if (!(pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)
          GetProcAddress(hModJS, "InternetInitializeAutoProxyDll")) ||
        !(pInternetDeInitializeAutoProxyDll =
          (pfnInternetDeInitializeAutoProxyDll)
          GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll"))
        || !(pInternetGetProxyInfo = (pfnInternetGetProxyInfo)
             GetProcAddress(hModJS, "InternetGetProxyInfo")))
        reportFuncErr("GetProcAddress");

    if (!DetectAutoProxyUrl(WPADLocation, sizeof (WPADLocation),
                            PROXY_AUTO_DETECT_TYPE_DHCP |
                            PROXY_AUTO_DETECT_TYPE_DNS_A))
        reportFuncErr("DetectAutoProxyUrl");

    printf("\n  WPAD Location is: %s\n", WPADLocation);

    GetTempPathA(sizeof (TempPath) / sizeof (TempPath[0]), TempPath);
    GetTempFileNameA(TempPath, NULL, 0, TempFile);
    URLDownloadToFileA(NULL, WPADLocation, TempFile, 0, NULL);

    if (!(returnVal = pInternetInitializeAutoProxyDll(0, TempFile,
                                                      NULL,
                                                      &HelperFunctions, NULL)))
        reportFuncErr("InternetInitializeAutoProxyDll");
    printf("\n  InternetInitializeAutoProxyDll returned: %d\n", returnVal);

    // Delete the temporary file
    // (or, to examine the auto-config script, comment out the
    // file delete and substitute the following printf call)
    // printf( "\n  The auto-config script temporary file is:\n    %s\n", TempFile);
    DeleteFileA(TempFile);

    if (!pInternetGetProxyInfo((LPSTR) url, strlen (url),
                               (LPSTR) host, strlen (host),
                               &proxy, &dwProxyHostNameLength))
        reportFuncErr("InternetGetProxyInfo");

    printf("\n  Proxy is: %s\n", proxy);

    if (!pInternetDeInitializeAutoProxyDll(NULL, 0))
        reportFuncErr("InternetDeInitializeAutoProxyDll");

    printf("\n...end of test program!\n");
    return (0);
}

Send comments about this topic to Microsoft

Build date: 11/19/2009

Tags :


Community Content

jpsanders
AutoProxyHelperFunctions do not have to be defined
You can pass NULL for the AutoProxyHelperFunctions argument. This will then use the built in AutoproxyHelperFunctions. Not all of then seem to be defined however. I will update this on which ones are available and which are not.
Tags :

guilleamodeo
Making this example work on Internet Explorer 7 and above
IE 7 and 8 calls the ...Ex versions of the functions --which in this example are NULL-- because are supposed to work with IPv6, although you can set them as the same as nonEx, all but one: IsInNetEx() beacause it takes different parameters than IsInNet(). The result would be that it will work as long as you do not use IPv6.... Here is how you setup

AutoProxyHelperVtbl OurVtbl = {
IsResolvable,
GetIPAddress,
ResolveHostName,
IsInNet,
        IsResolvable,
GetIPAddress,
ResolveHostName,
IsInNetEx,
NULL // No need to set SortIPList()
};


Here is an implementation of IsInNetEx() that would work for IPv4.. ;-)


BOOL __stdcall jsIsInNetEx(LPSTR lpszIPAddress,LPSTR lpszPrefix) {
DWORD dst,src,msk=0xFFFFFFFF;
CHAR *ptr,lpszDest[256];
int shf=0;

if(lpszIPAddress==NULL || lpszPrefix==NULL)
return FALSE;

if(strchr(lpszIPAddress,':') || strchr(lpszPrefix,':')) {
// DOESN'T YET SUPPORT IPv6
return FALSE;
}
memset(lpszDest,0,sizeof(lpszDest));
strncpy(lpszDest,lpszPrefix,255);
ptr=(CHAR *)strchr(lpszDest,'/');
if(ptr!=NULL) {
*ptr++='\0';
shf=32 - atoi(ptr);
}
msk<<=shf;
src=inet_addr(lpszIPAddress);
dst=inet_addr(lpszDest);
return ((src & msk)==(dst & msk )) ? TRUE : FALSE;
}

Tags :

guilleamodeo
InternetGetProxyInfo() Crashes
I was experiencing a crash every time I called InternetGetProxyInfo() and it turned out to be a threading problem.

I called IntenernetInitializeAutoProxyDll() with the PAC script just once on the main thread, and then InternetGetProxyInfo() in each thread that required it, but it crashed. I changed the code to just load the DLL on the main thread, and then Call IntenernetInitializeAutoProxyDll(), InternetGetProxyInfo() and IntenernetDeinitializeAutoProxyDll() on each thread and that did the trick... :-)

It seems that IntenernetInitializeAutoProxyDll() have something which make it thread-dependant... ...weird. :-(
Tags :

Page view tracker