Monday, April 26, 2010

WinHTTP, .net, IE and Proxy Server Fun

Today, I was working on finalizing a solution for proxy auto configuration for the company I work for. The script that I had worked great with Firefox and Internet Explorer 6 and up. I found out quickly however, it did not work with WinHTTP or .net automatic proxy configuration. Anything dependent on either one of those got a proxy or went directly to the resource when it shouldn't have. Of course, this wouldn't fly.

After many hours of trial and error I came to the conclusion that WinHTTP and .net behave differently from their browser counterparts in a very significant way. When executing the script in a PAC file, browsers do not need to have the host wrapped in the dnsResolve function. WinHTTP and .net do need this however in order to properly determine when to use a proxy server.

Here is original proxy script I had that worked only with browsers:
function FindProxyForURL(url, host)
{
var myIPAddr = myIpAddress();

// Bypass proxy settings for internal domains and private IP ranges
if (isInNet(host, "10.0.0.0", "255.0.0.0") ||
isInNet(host, "192.168.0.0", "255.255.0.0") ||
isInNet(host, "172.16.0.0", "255.240.0.0") ||
isInNet(host, "127.0.0.1", "255.255.255.255") ||
isPlainHostName(host) ||
dnsDomainIs(host, ".domain-one.example.com") ||
dnsDomainIs(host, ".domain-two.example.com") ||
dnsDomainIs(host, ".domain-three.example.com") ||
dnsDomainIs(host, ".domain-four.example.com"))
{
return "DIRECT";
}
// Location 1 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.1.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.2.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.3.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.4.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.5.0.0", "255.255.0.0") )
{
return "PROXY PROXYSERV1:8080; DIRECT";
}
// Location 2 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.6.0.0", "255.255.0.0"))
{
return "PROXY PROXYSERV2:8080; DIRECT";
}
// Location 3 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.7.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.8.0.0", "255.255.0.0"))
{
return "PROXY PROXYSERV3:8080; DIRECT";
}

// Catch all for locations without proxy servers
else
{
return "DIRECT";
}
}

Here is the final PAC file that works with browsers, WinHTTP and .net:

function FindProxyForURL(url, host)
{
var ipAddr = dnsResolve (host);
var myIPAddr = myIpAddress();

// Bypass proxy settings for internal domains and private IP ranges
if (isInNet(ipAddr, "10.0.0.0", "255.0.0.0") ||
isInNet(ipAddr, "192.168.0.0", "255.255.0.0") ||
isInNet(ipAddr, "172.16.0.0", "255.240.0.0") ||
isInNet(ipAddr, "127.0.0.1", "255.255.255.255") ||
isPlainHostName(host) ||
dnsDomainIs(host, ".domain-one.example.com") ||
dnsDomainIs(host, ".domain-two.example.com") ||
dnsDomainIs(host, ".domain-three.example.com") ||
dnsDomainIs(host, ".domain-four.example.com"))
{
return "DIRECT";
}
// Location 1 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.1.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.2.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.3.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.4.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.5.0.0", "255.255.0.0") )
{
return "PROXY PROXYSERV1:8080; DIRECT";
}
// Location 2 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.6.0.0", "255.255.0.0"))
{
return "PROXY PROXYSERV2:8080; DIRECT";
}
// Location 3 - Use Proxy server first then use direct if necessary
else if (isInNet(myIPAddr, "10.7.0.0", "255.255.0.0") ||
isInNet(myIPAddr, "10.8.0.0", "255.255.0.0"))
{
return "PROXY PROXYSERV3:8080; DIRECT";
}

// Catch all for locations without proxy servers
else
{
return "DIRECT";
}
}

The differences are subtle but ever so important while implementing a proxy server auto configuration solution. Ensuring that WinHTTP and .net work like the browser should help ensure you don't get caught with servers/services and even client applications improperly using a proxy server. Of course with servers, the best option is to configure the WinHTTP options manually. This prevents the service from taking the performance penalty associated with automatic proxy discovery.

To manually configure the WinHTTP proxy server on computer you can either use proxycfg.exe or my preferred method of netsh. To set the proxy server and exclusions with netsh you can run the following command from the command prompt.

netsh winhttp set proxy proxyserver:8080 "<local>;server.domain.example.com"

The <local> entry tells WinHTTP to bypass the proxy for servers which use a relative name. Don't forget to run that command prompt as an administrator on Vista/7/2008/2008 R2 machines or you will get an access denied error.

If you ever need to clear out the WinHTTP proxy information you can run this command (again, as an administrator):

netsh winhttp reset proxy

And thus concludes my proxy server auto configuration odyssey.

No comments:

Post a Comment