A pac file is a javascript-file written to automatically configure browsers for use with proxies. It was primarily intended for use in larger networks, but is also useful for laptops that roam several locations. This file is also used as a WPAD-file, short for Web Proxy Autodiscovery Protocol.
The file might reside either on the network, on a local disk or in the case of being used as WPAD, on a webserver. The use of dns and dhcp for locating proxy settings requires the browser to have “Automatically detect settings” selected. Going this route is problematic in several ways, both because of the way clients attempt to locate the pac file and because of the time involved locating settings in this fashion.
The browser will first check if dhcp-option 252 is configured and use the string provided. If there is no such entry the next check is against the primary dns, looking for the wpad host (in several iterations based on primary dns-suffix). If found, it then continues to retrieve wpad.dat from the host over http, expecting the mime-type of “application/x-ns-proxy-autoconfig”. (details)
This method is both error prone as well as it leaves quite a bit wanting in terms of security (what happens if someone checks in a host to your network with a malicious file?). When we outsourced the scanning of mail- and web-content at work lately we chose to go another route, because of these insufficiencies.
The use of pac-files is intriguing, because of the flexibility they present in choosing how to connect to the web. As we don’t want to put our pac online on a webserver for anyone to access, nor are we interested in the time needed to fetch it, we opted for placing the pac locally on the computer.
To distribute the settings at work I used a combination of GPO’s, a vbscript that updates the pac, and the NETLOGON fileshare on our DC’s. The GPO configures Internet connection settings for IE, and launches a vbscript upon logon that updates the local copy of the pac-file if the network version have a newer modify-stamp than the local file have.
The pac file itself have a few predefined functions and variables exposed, where I the most useful ones these:
- host – host being connected to.
- url – address to be retrieved.
- myIpAddress() – returns the IP address (in integer-dot format) of the network adapter that have the highest priority on the host, that the browser is running on.
- isInNet(host, network, subnet) – determines if the given host resides on the specified network.
- dnsDomainIs(host, dns-suffix) – attempts to decide if host belongs to domain.
- isPlainHostName(host) - returns true if host doesn’t contain any dots.
Basically, what I needed from my pac, was for it to decide what network segment the client resided on (if any), and connect to the appropriate proxy (if not accessing a resource on the Intranet).
To archive this, my pac-file ended up more or less like this:
- UPDATE: Symantec MessageLabs provides an excellent solution for solving content filtering and viral threats from the net, and I’ve had a very positive experience using their services. Building on their template pac, the following is now what is in use. It should be fairly easy to customize it for an enviroment without them too;)
// ********************************************************************
// template.txt: Version 3.1
// slight edits for clarity
//
// Proxy Auto-Config (PAC) template file for web browser and roaming
// users. Follow the instruction through this configuration file to
// update for your specific environment.
//
// Notes:
// - "host" refers to the host portion of the URL being requested (i.e.
// everything after the :// at the beginning of the URL up to the
// first colon (:) or slash (/) (e.g. www.example.com).
// - "url" refers to the entire URL being requested. This includes
// the protocol and file (e.g. http://www.example.com/index.html).
// - Microsoft IE processes the PAC file once per hostname and caches
// the result. You cannot have different behaviour for the same
// hostname (e.g. http://www.example.com/index.html must be
// directed to the same proxy as http://www.example.com/foo.html).
// - isInNet will perform a DNS lookup for non IP addresses. Ensure the
// host is a raw IP before using this function.
// - For debugging, set the debug variable to true.
// ********************************************************************
function FindProxyForURL(url, host)
{
var debug = true;
var direct = "DIRECT";
// Proxy addresses by region.
var proxy1_eu = "PROXY proxy1.eu.webscanningservice.com:3128";
var proxy1_us = "PROXY proxy1.us.webscanningservice.com:3128";
var proxy2_us = "PROXY proxy2.us.webscanningservice.com:3128";
var proxy1_ap = "PROXY proxy1.ap.webscanningservice.com:3128";
var proxy1_hk = "PROXY proxy1.hk.webscanningservice.com:3128";
// *****************************************************************
// Proxy address for roaming users, specify the appropriate region
// *****************************************************************
var roaming1_eu = "PROXY roaming1.eu.webscanningservice.com:80";
var roaming1_us = "PROXY roaming1.us.webscanningservice.com:80";
var roaming2_us = "PROXY roaming2.us.webscanningservice.com:80";
var roaming1_ap = "PROXY roaming1.ap.webscanningservice.com:80";
var roaming = roaming1_eu;
// *****************************************************************
// Specify your CSP address if applicable, one line for each
// distinct company subnet.
// *****************************************************************
var site1 = "PROXY 192.168.2.10:3128";
var site3 = "PROXY 192.168.105.20:3128";
var site2 = "PROXY 192.168.1.3:8080";
// Source IP address.
var myIp = myIpAddress();
var anet = "255.0.0.0"
var bnet = "255.255.0.0"
var cnet = "255.255.255.0"
// If the host is this computer, connect directly
if ((host == "localhost") ||
(host == "localhost.localdomain") ||
(host == "127.0.0.1"))
{
if (debug) alert("PAC: DIRECT: localhost: " + host);
return direct;
}
// If host name is local (i.e. contains no dots), connect directly.
if (isPlainHostName(host))
{
if (debug) alert("PAC: DIRECT: plain host: " + host);
return direct;
}
// If host name is part of the IANA private IP address ranges, connect
// directly.
if (/^\d+\.\d+\.\d+\.\d+$/.test(host) &&
(isInNet(host, "10.0.0.0", anet) ||
isInNet(host, "169.0.0.0", anet) ||
isInNet(host, "172.16.0.0", "255.240.0.0") ||
isInNet(host, "192.168.0.0", bnet)))
{
if (debug) alert("PAC: DIRECT: IANA private network: " + host);
return direct;
}
// *****************************************************************
// Specify remote URLs that are trusted and don't require proxying
// and should be bypassed when roaming.
// *****************************************************************
if (shExpMatch(host, "*.download.microsoft.com") ||
shExpMatch(host, "*.windowsupdate.com") ||
shExpMatch(host, "*.windowsupdate.microsoft.com") ||
shExpMatch(host, "windowsupdate.microsoft.com") ||
shExpMatch(host, "*.update.microsoft.com") ||
shExpMatch(host, "update.microsoft.com"))
{
if (debug) alert("PAC: BYPASS: Windows Update: " + host);
roaming = direct;
}
// *****************************************************************
// Specify VPN ranges, one line for each VPN range.
// When using a VPN, proxying is done through roaming proxy.
// *****************************************************************
// if (isInNet(myIp, "<VPN IP 1>", "<VPN Mask>" )) { if(debug) alert("PAC: ROAMING: VPN1: " + host); return roaming; }
// if (isInNet(myIp, "<VPN IP 2>", "<VPN Mask>" )) { if(debug) alert("PAC: ROAMING: VPN1: " + host); return roaming; }
// *****************************************************************
// Specify local FQDNs which do not require proxying, one line per
// expression. Shell expression patterns can be used.
// *****************************************************************
// if (shExpMatch(host, "<Local FQDN 1>")) { if(debug) alert("PAC: ROAMING: Local FQDN 1: " + host); return direct; }
// if (shExpMatch(host, "<Local FQDN 2>")) { if(debug) alert("PAC: ROAMING: Local FQDN 1: " + host); return direct; }
// *****************************************************************
// Specify company subnet source IP address ranges which require
// proxying, one line per expression. Specify adequate proxy region
// or CSP address for each range.
// *****************************************************************
// if (isInNet(myIp, "<Subnet IP 1>", "<Subnet Mask>")) { if(debug) alert("PAC: ROAMING: Subnet 1: " + host); return <proxy_region1>; }
if (isInNet(myIp,"192.168.2.0", cnet)) {if(debug) alert("PAC: proxy site 1: " + host); return site1; }
if (isInNet(myIp,"192.168.112.0", cnet)) {if(debug) alert("PAC: site without local proxy: " + host); return roaming;}
if (isInNet(myIp,"192.168.104.0", cnet)) {if(debug) alert("PAC: proxy site2: " + host); return site2; }
if (isInNet(myIp,"192.168.105.0", cnet)) {if(debug) alert("PAC: proxy site3: " + host); return site3; }
// When outside company subnet, connect to roaming proxy.
if (debug && roaming != direct) alert("PAC: ROAMING: Default: " + host);
return roaming;
}
To recap, pac files are good for determining what proxy to use, as long as:
- your confident that the network adapter priority is sane. For me this means this from the top down: Cisco VPNvirtual interface, Wired Lan, Wireless Lan, RAS and last the RNDIS-connector from windows mobile (to set this up, go to control panel->Network Connections->Advanced Menu in windows explorer->Advanced Setting, from there select the nic and use the up/down arrows)
- You don’t use Google Chrome
- Deploy config using some other method than dns/dhcp – they are both slow an insecure.
references/sources:
http://www.findproxyforurl.com/
Web Proxy Auto-Discovery Protocol draft-cooper-webi-wpad-00.txt