11.02.2013

WPAD и PAC файл

    Обобщение и перевод официальной информации на тему получения автоматического определения настроек прокси.

WPAD -
Web Proxy Auto-Discovery Protocol (протокол автоматической настройки прокси) — метод, используемый клиентами для определения места (URL) расположения конфигурационного файла с использованием технологий DHCP и/или DNS. wiki

PAC-файл - Proxy Auto Configuration файл настроек прокси для браузера на javascript (wpad.dat, proxy.pac).

По умолчанию браузер ищет файл настроек wpad.dat на wpad.domain.loc (если машина в домене domain.loc) по web.

Настройка WPAD 


Порядок поиска файла настроек proxy:
  1. DHCP
  2. SLP
  3. DNS A / CNAME
  4. DNS SRV
  5. DNS TXT

DHCP

В глобальной конфигурации прописываем:
option wpad code 252 = text;
option wpad "http://wpad.domain.loc/wpad.dat\000";

DNS

Создаем A или CNAME запись для хоста wpad:
wpad            A        192.168.10.181
или
wpad            CNAME    server1
Создаем SRV и TXT записи:
_wpad._tcp      SRV      0 0 80 wpad.domain.loc.
wpad            TXT      "service: wpad:http://wpad.domain.loc/wpad.dat"

Внимание! TXT запись нельзя использовать с CNAME, менйте на A запись.

Web сервер

На сервер где лежит wpad.dat если отсутствует строчка в файле mime.type (apache: /etc/mime.type, nginx: /etc/nginx/mime.types):
application/x-ns-proxy-autoconfig  pac dat
После перегружаем web сервер.  


PAC файл

Функции и переменные

isPlainHostName(host)

host - имя хоста из URL (без указания порта)

Истина, если имя хоста (без точек), а не доменное имя.

Пример:

isPlainHostName("www")
    is true.
isPlainHostName("www.netscape.com")
    is false.

dnsDomainIs(host, domain)

host - имя хоста из URL
domain - доменное имя

Возвращает true, если имя хоста совпадает с доменом.

Пример:
dnsDomainIs("www.netscape.com", ".netscape.com")
    is true.
dnsDomainIs("www", ".netscape.com")
    is false.
dnsDomainIs("www.mcom.com", ".netscape.com")
    is false. 

localHostOrDomainIs(host, hostdom)

host - имя хоста из URL.
hostdom - полное имя хоста для сравнения.

Истинно, если имя хоста в точности совпадает с указанным именем хоста, или если нет части доменного имени в имени хоста, но имя хоста совпадает с неопределенным доменом.

Пример:

localHostOrDomainIs("www.netscape.com", "www.netscape.com")
    is true (точное совпадение).
localHostOrDomainIs("www", "www.netscape.com")
    is true (имя хоста совпало, домен не определен).
localHostOrDomainIs("www.mcom.com", "www.netscape.com")
    is false (доменное имя не совпадает).
localHostOrDomainIs("home.netscape.com", "www.netscape.com")
    is false (имя хоста не совпадает).

isResolvable(host)

host - имя хоста из URL.

Если dns резолвит имя хоста, то истина.

Пример:

isResolvable("www.netscape.com")
    is true
isResolvable("bogus.domain.foobar")
    is false.

isInNet(host, pattern, mask)

host - DNS имя или IP адрес. Если указанно имя, то оно будет преобразовано в IP адрес
pattern - шаблон IP разделенный точками
mask - маска для шаблона IP, 0 - игнорирует, 255 - совпадает.

Истинно если IP хоста совпадает с шаблоном.

Пример:
isInNet(host, "198.95.249.79", "255.255.255.255")
    истинно если имя хоста с точностью совпадает с IP 198.95.249.79.
isInNet(host, "198.95.0.0", "255.255.0.0")
    истинно если хост совпадет по маске 198.95.*.*.

dnsResolve(host)

host - имя хоста для резолвинга.

Возвращает IP адрес в виде строки разделенной точками.

Пример:
dnsResolve("home.netscape.com")
    вернет строку "198.95.249.79"

myIpAddress()

Возвращает IP адрес хоста где был запущен браузер в виде строки разделенной точками.

Пример:
myIpAddress()
    вернет строку "198.95.249.79" если браузер запущен на этом хосте.

dnsDomainLevels(host)

host - имя хоста из URL.

Возвращает число (integer) уровня домена (число точек) в имени.

Пример:
dnsDomainLevels("www")
    вернет 0.
dnsDomainLevels("www.netscape.com")
    вернет 2.

shExpMatch(str, shexp)

str - любая строка для сравнения (может быть URL, или hostname).
shexp - шаблон для сравнения.

Возвращает истину если строка совпадает с шаблоном.
Шаблон является оболочкой, а не регулярным выражением.

Пример:
shExpMatch("http://home.netscape.com/people/ari/index.html", "*/ari/*")
    is true.
shExpMatch("http://home.netscape.com/people/montulli/index.html", "*/ari/*")
    is false.

weekdayRange(wd1, wd2, gmt)

wd1 и wd2 - один из дней недели:
   SUN MON TUE WED THU FRI SAT
gmt - является строкой: GMT или может быть опущена.

Только первый параметр является обязательным.
Если указан один параметр, то сравнивается один день, если оба параметра то период между ними. Если указан GMT, то берется время GMT, иначе локальное.

Пример:
weekdayRange("MON", "FRI")
    истинно с понедельника по пятницу (местный часовой пояс).
weekdayRange("MON", "FRI", "GMT")
    тоже самое, но GMT часовой пояс.
weekdayRange("SAT")
    истинно только в субботу по местному времени.
weekdayRange("SAT", "GMT")
    истинно в субботу GMT время.
weekdayRange("FRI", "MON")
    истинно с пятницу по понедельник (важен порядок!).

dateRange(day)
dateRange(day1, day2)
dateRange(mon)
dateRange(month1, month2)
dateRange(year)
dateRange(year1, year2)
dateRange(day1, month1, day2, month2)
dateRange(month1, year1, month2, year2)
dateRange(day1, month1, year1, day2, month2, year2)
dateRange(day1, month1, year1, day2, month2, year2, gmt) 


day - число месяца между 1 и 31.
month - один из месяцев:
   JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
year - год, для примера 1995 (но не 95).
gmt - является строкой "GMT", определяет GMT временную зону; если опущена, то местная временная зона.

Указывать можно один параметр день, месяц, год, то сравнивается один день, месяц или год, если оба параметра то период между ними. Если указан GMT, то берется время GMT, иначе локальное.

Пример:
dateRange(1)
    первый день каждого месяца, местное время.
dateRange(1, "GMT")
    первый день каждого месяца, GMT зона.
dateRange(1, 15)
    первая половина каждого месяца.
dateRange(24, "DEC")
    24 декабря каждого года.
dateRange(24, "DEC", 1995)
    24 декабря 1995 года.
dateRange("JAN", "MAR")
    первый квартал каждого года.
dateRange(1, "JUN", 15, "AUG")
    с 1 июня по 15 августа каждого года (включая 1 июня и 15 августа).
dateRange(1, "JUN", 15, 1995, "AUG", 1995)
    с 1 июня по 15 августа 1995 года.
dateRange("OCT", 1995, "MAR", 1996)
    с октября 1995 по март 1996 (включая введенные месяцы).
dateRange(1995)
    весь 1995 год.
dateRange(1995, 1997)
   с 1995 по 1997 г.г. 

timeRange(hour)
timeRange(hour1, hour2)
timeRange(hour1, min1, hour2, min2)
timeRange(hour1, min1, sec1, hour2, min2, sec2)
timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)

hour - час (от 0 по 23).
min - минуты (от 0 по 59).
sec - секунды (от 0 по 59).
gmt - является строкой "GMT", определяет GMT временную зону; если опущена, то местная временная зона.

Истинно в указанное (или в интервале) время.

Пример:
timerange(12)
    в течении 12-го часа.
timerange(12, 13)
    аналогично как выше.
timerange(12, "GMT")
    в течении 12-го часа, в GMT зоне.
timerange(9, 17)
    с 9 до 17 часов.
timerange(8, 30, 17, 00)
    между 8:30 и 17:00.
timerange(0, 0, 0, 0, 0, 30)
    между 00:00:00 и 00:00:30 (в течении 30 секунд)

Примеры

Использовать прокси для всех исключая локальные адреса
Если URL страницы ведет на адрес с хостом .domain.loc, то пускаем напрямую, иначе через прокси w3proxy.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if (isPlainHostName(host) ||
            dnsDomainIs(host, ".domain.loc"))
            return "DIRECT";
        else
            return "PROXY w3proxy.domain.loc:8080; DIRECT";
    }

Как и выше, но и использовать прокси для локальных серверов, которые находятся за пределами брандмауэра

Если страница ведет на хост или домен .domain.loc и не совпадает с www.domain.loc или merchant.domain.loc, то пропускаем напрямую, иначе через прокси w3proxy.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if ((isPlainHostName(host) ||
             dnsDomainIs(host, ".netscape.com")) &&
            !localHostOrDomainIs(host, "www.netscape.com") &&
            !localHostOrDoaminIs(host, "merchant.netscape.com"))
 
            return "DIRECT";
        else
            return "PROXY w3proxy.netscape.com:8080; DIRECT";
    }

Использовать прокси только тогда, когда не резолвится адрес

function FindProxyForURL(url, host)
    {
        if (isResolvable(host))
            return "DIRECT";
        else
            return "PROXY proxy.domain.loc:8080";
    }
Пример выше требует постоянного обращения к DNS, чего можно избежать если сгруппировать правила и последним проверять через DNS:
Если страница ведет на хост или домен .domain.loc или резолвится, то напрямую, иначе через прокси proxy.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if (isPlainHostName(host) ||
            dnsDomainIs(host, ".domain.loc") ||
            isResolvable(host))
            return "DIRECT";
        else
            return "PROXY proxy.domain.loc:8080";
    }

Решение на основе подсетей

Если IP адрес запрошенного ресурса входит в подсеть 198.95.*.*, то напрямую иначе через прокси proxy.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if (isInNet(host, "198.95.0.0", "255.255.0.0"))
            return "DIRECT";
        else
            return "PROXY proxy.mydomain.com:8080";
    }
Приводим к минимуму обращения к DNS:
Если страница ведет на хост или домен .domain.com или входит в подсеть 198.95.*.*, то напрямую иначе через прокси proxy.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if (isPlainHostName(host) ||
            dnsDomainIs(host, ".domain.com") ||
            isInNet(host, "198.95.0.0", "255.255.0.0"))
            return "DIRECT";
        else
            return "PROXY proxy.domain.loc:8080";
    }

Балансировка нагрузки на основе шаблонов URL

Есть четыре прокси-сервера, один из которых для горячего резервирования всех остальных,если любой из оставшихся трех упадет, то четвертый возьмет трафик на себя. Кроме того, три оставшихся прокси-сервера распределяют нагрузку на основе шаблонов URL.
Если страница ведет на хост или домен .domain.loc - пропускаем на прямую,
иначе если страница ведет на *.com - пропускаем через прокси proxy1.domain.loc:8080, если он не доступен, то через proxy4.domain.loc:8080,
иначе если страница ведет на *.edu - пропускаем через прокси proxy2.domain.loc:8080, если он не доступен, то через proxy4.domain.loc:8080,
иначе все остальные страницы пропускаем через прокси proxy3.domain.loc:8080, если он не доступен, то через proxy4.domain.loc:8080
function FindProxyForURL(url, host)
    {
        if (isPlainHostName(host) || dnsDomainIs(host, ".domain.loc"))
            return "DIRECT";
        else if (shExpMatch(host, "*.com"))
            return "PROXY proxy1.domain.loc:8080; " +
                   "PROXY proxy4.domain.loc:8080";
        else if (shExpMatch(host, "*.edu"))
            return "PROXY proxy2.domain.loc:8080; " +
                   "PROXY proxy4.domain.loc:8080";
        else
            return "PROXY proxy3.domain.loc:8080; " +
                   "PROXY proxy4.domain.loc:8080";
    }

Настройка прокси для определенного протокола

Для определения протокола используется javascript функция substring().
Если URL начинается на http: - пропускаем через http-proxy.domain.loc:8080,
если на ftp: - через ftp-proxy.domain.loc:8080,
если на gopher: - через gopher.domain.loc:8080,
если на https: или snews: - через security-proxy.domain.loc:8080,
всё остальное на прямую
function FindProxyForURL(url, host)
        {
            if (url.substring(0, 5) == "http:") {
 
                return "PROXY http-proxy.domain.loc:8080";
            }
            else if (url.substring(0, 4) == "ftp:") {
 
                return "PROXY ftp-proxy.domain.loc:8080";
            }
            else if (url.substring(0, 7) == "gopher:") {
 
                return "PROXY gopher-proxy.domain.loc:8080";
            }
            else if (url.substring(0, 6) == "https:" ||
                     url.substring(0, 6) == "snews:") {
 
                return "PROXY security-proxy.domain.loc:8080";
            }
            else {
 
                return "DIRECT";
            }
        }
тоже самое можно сделать с помощью функции shExpMatch():
...
        if (shExpMatch(url, "http:*")) {
            return "PROXY http-proxy.domain.loc:8080;
        }
...

Ссылки:
http://tools.ietf.org/html/draft-ietf-wrec-wpad-01
http://web.archive.org/web/20070602031929/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html

1 comment:

влад said...

Здравствуйте! Подскажите по поводу "Настройка прокси для определенного протокола". IE не ходит через прокси на страницы https. Мой файл wpad.dat

function FindProxyForURL(url, host)
{
if (isPlainHostName(host) ||
dnsDomainIs(host, ".home-router", ".i2p", ".onion") ||
isInNet(host, "192.168.0.0", "255.255.0.0") ||
isInNet(host, "127.0.0.0", "255.255.0.0"))
return "DIRECT";
else if (url.substring(0, 6) == "https:")
return "PROXY 192.168.0.1:3128";
}
Раньше после else был сразу return. FF работает что с if (url.substring(0, 6) == "https:") с без неё. IE игнорирует.