Hi Team,

when creating an automatic proxy-configuration script (PAC-filer or also known as wpad.dat), questions arrive on how these could be optimized in order to speed up their performance

The functions which can be used in order to evaluate an address (URL and hostname) are explained in the following article:
JavaScript or JScript Auto-Proxy Example Files
http://technet.microsoft.com/library/Dd361950

As mentioned in that article, the functions  isInNet(), isResolvable() and dnsResolve() initiate queries to the DNS-subsystem.
Therefore the usage of these functions should be avoided, when possible or at least reduced.

1. Query for NetBIOS-names
NetBIOS-names (servernames with no dot in their name) no are typically used in the intranet only and are therefore not routed through the proxy.
  if (isPlainHostName(host))
    return "DIRECT";

2. Query for internal DNS-suffixes
Internally used DNS-zones are normally routed directly. The easiest way to determine such hosts is done by using the function dnsDomainis:
  if (dnsDomainIs (host, ".dns.company.com"))
    return "DIRECT";

The faster method for the same result can be done by using ShExMatch(), which performs a string compare. So the same result with the function above, where the “*”-character is then used as wildcard:
  if (shExpMatch(host, "*.dns.company.com"))
    return "DIRECT";

3.  Query for IP-ranges
The idea for that rule is to check, if the IP-address of the host belongs to the local intranet, regardless to the name of the webserver, which should bypass the proxy in order to navigate directly to the it.

In case, that the IP-address had been entered directly in the address-bar there is no need to resolve it again. You can use the following code in order to check, if the host has already the format of an IP-address :
  var isIpV4Addr = /^(\d+.){3}\d+$/;
  ret = isIpV4Addr.test (host);
This routine checks if the variable host contains 3 numbers which are followed by a dot, and if another number is followed- The result of this check is then passed to the variable ret, which is true in case of an IP, and false – if otherwise.

This would be be the codesnip where the variable hostIP will contain the IP-address for additional checks later:
  var hostIP;
  var isIpV4Addr = /^(\d+.){3}\d+$/;
  if (isIpV4Addr.test (host))
    hostIP=host;
  else
    hostIP=dnsResolve (host);

When a non-existing host had been passed to the function (e.g. cause the user entered something wrong in the address bar), the result in hostIP might be 0. Any additional errorhandling could be done by the proxy:
  if (hostIP==0)
    return "PROXY myproxy:80";

Now, as we have the IP-address of the host, the checks for the internal IP-ranges needs to be done.
When possible, use the shExpMatch-function instead of isInNet. The following two codesnips have the identical result, while shExpMatch is faster in execution:
  if (isInNet (hostIP, "95.53.0.0", "255.255.0.0"))
    return "DIRECT";
  if (shExpMatch (hostIP, "95.53.*))
    return "DIRECT";

4. Javascript is case-sensitive
The proxyscript uses the language javascript, which is case-sensitve. Therefore an if-clause where upper characters are used will never turn true, while the other parameter is using lowercase.
Internet Explorer itself converts the variables host and url into lowercase before the function  FindProxyForURL is called.
This is not true for WinHTTP, which passes the hoist and the url directly to the function.
Therefore the parameters, which are checked within the PAC-file should be converted within the PAC befotre they are evaluated. Here is the call for the convert:
    host = host.toLowerCase();

5. Use of IPv6
In case that you want to use and handle IPv6-addresses, Internet Explorer supports them since IE7 on every OS-Version (and WinHTTP since Windows Vista), but you then need to use “Ex“-functions (like isInNetEx ()) as mentioned in the following Blogpost:

WinINet and WinHTTP IPv6 Support in Web Proxy Auto-Discovery (WPAD) scripts enabled in Windows Vista
http://blogs.msdn.com/b/wndp/archive/2006/07/18/ipv6-wpad-for-winhttp-and-wininet.aspx

One example, where the implementation of myIpAddressEx was very useful is also mentioned in the KB-article http://support.microsoft.com/kb/2839111/en-us

6. Testing of a PAC-file
In case that the script contains any syntax-error (e.g. a missing ‘)’-character in an if-statement, the script is no more executed. In order to minimize such errors, you may consider the usage of a script-editor which performs syntax-checking on the fly. When using Visual Studio, you can just rename the extension of your PAC-file to JS when editing.

After this, you can test it by configuring it in IE as a local PAC-file. For the local C:-drive the syntax in order to configure IE would be file://c:\test.pac 
With IE11, the usage of a PAC-file through the file-protocol is no more possible, unless you add the following registry-key

[HKLM\SOFTWARE\Policies\Microsoft\Windows\CurrentVersion\Internet Settings]
(DWORD)"EnableLegacyAutoProxyFeatures"=1

More information to the usage of local PAC-files is available in the following Blogpost:

When debugging and testing the PAC-file, you can add statements as extraline which will initiate a popup, when the line is hit:
alert ("We are now here and host is: " + host);

Of course, alert ()-statements should never be active within the production environment.

Note: alert-statements will no more appear when using Windows 8 or higher!

 

6.1 Testing with autoprox.exe [ DOWNLOAD AUTOPROXY ]
Sometimes you need just to test your PAC-file, if the expected route is returned, although you have no access to the website in question. For such testing you can use the (attached) command line-utility tool autoprox.exe, which my colleague Pierre-Louis Coll created.
When starting it in a CMD without additional parameter the usage is displayed:

C:\temp>autoprox
Version : 2.1.0.0
Written by pierrelc@microsoft.com
Usage : AUTOPROX -s  (calling DetectAutoProxyUrl and saving wpad.dat file in temporary file)
Usage : AUTOPROX  [-h] url [Path to autoproxy file]
       -h: calls InternetInitializeAutoProxyDll with helper functions implemented in AUTOPROX
AUTOPROX url: calling DetectAutoProxyUrl and using WPAD.DAT logic to find the proxy for the url
AUTOPROX url path: using the autoproxy file from the path to find proxy for the url
Example: autoprox -s
Example: autoprox http://www.microsoft.com
Example: autoprox -h http://www.microsoft.com c:\inetpub\wwwroot\wpad.dat
Example: autoprox http://www.microsoft.com http://proxy/wpad.dat

Here is the output with our sample:

C:\temp>autoprox http://us.msn.com c:\temp\sample.pac
The Winsock 2.2 dll was found okay
url: http://us.msn.com
autoproxy file path is : c:\temp\sample.pac
Calling InternetInitializeAutoProxyDll with c:\temp\sample.pac
        Calling InternetGetProxyInfo with url http://us.msn.com and host us.msn.com
        Proxy returned for url http://us.msn.com is:
PROXY myproxy:80;

When you want to see which DNS-related functions have been called, you can use the parameter “-h” in addition: 
Here the output, when this is used:

C:\temp>autoprox -h http://us.msn.com c:\temp\sample.pac
The Winsock 2.2 dll was found okay
Will call InternetInitializeAutoProxyDll with helper functions
url: http://us.msn.com
autoproxy file path is : c:\temp\sample.pac
Calling InternetInitializeAutoProxyDll with c:\temp\sample.pac
        Calling InternetGetProxyInfo with url http://us.msn.com and host us.msn.com
ResolveHostByName called with lpszHostName: us.msn.com
ResolveHostByName returning lpszIPAddress: 65.55.206.229
        Proxy returned for url http://us.msn.com is:
PROXY myproxy:80;

Error-Handling in autoprox.exe:
a) When you specify a non-existing PAC-file (e.g. typo in the command-line), the result from autoprox.exe will be:
  ERROR: InternetInitializeAutoProxyDll failed with error number 0x6 6.

b) When the Pac-file contains syntax-errors, you typically receive the following message displayed:
  ERROR: InternetGetProxyInfo failed with error number 0x3eb 1003.

After finishing the local test, the PAC-file should be copied to the webserver where it will be accessed through http-protocol.

Here would be the complete sample, as discussed above:

function FindProxyForURL(url,host)
{
  // NetBIOS-names
  if (isPlainHostName(host))
    return "DIRECT";
  // change to lower case – if not already been done
  host = host.toLowerCase();
  // internal DNS-suffixes
  if (shExpMatch(host, "*.corp.company.com") ||
      shExpMatch(host, "*.dns.company.com"))
    return "DIRECT";
  // Save the IP-address to variable hostIP
  var hostIP;
  var isIpV4Addr = /^(\d+.){3}\d+$/;
  if (isIpV4Addr.test (host))
    hostIP=host;
  else
    hostIP=dnsResolve (host);
  // IP could not be determined -> go to proxy
  if (hostIP==0)
    return "PROXY myproxy:80";
  // These 3 scopes are used only internally
  if (shExpMatch (hostIP, "95.53.*") ||
      shExpMatch (hostIP, "192.168.*") ||
      shExpMatch (hostIP, "127.0.0.1"))
    return "DIRECT";
  // Eveything else goes through the proxy
  return "PROXY myproxy:80;";
}

 

Here is a known issue:

 

Good Blog Article:

 

This blog has been provided to you by another one of our Escalation Engineers for Internet Explorer, Heiko Mayer.