文章目录

前言:翻回国的前提就是要购买国内的服务器,然后使用命令搭建,然后修改小火箭的PAC设置。

本教程需对其他教程熟悉,活对命令爬墙有一定理解。如需帮助,可加文中Q群联系群主,需要打赏方可远程协助!

详细步骤:

一、购买服务器

国内服务器:我只推荐腾讯云,新用户点此购买,腾讯云最新云服务器秒杀优惠

二、使用SSH工具连接云服务器

推荐使用xshell ,Putty也可以,Xshell可以在Q群文件下载,群号:831572998 ,Putty自己搜索下载

三、使用命令搭建

国外翻墙回国用SS或者SSR都可以

搭建教程:https://www.hostphb.com/we/2341.html  (注意参考教程说明,请从头阅读

四、修改PAC信息

到此,基本就可以了,客户端可以网上下载,或者群文件查找,在国外么都是可以搜索下载的,我就不单独发下载地址了

以下为参考信息:

手动规则修改

方法一:使用ACL规则(ACL4SSR)

以 Mac OS X 为例,运行客户端并连接服务器,点击小飞机图标,选择代理模式为ACL Mode菜单下的Proxy Back China

点击小飞机图标,找到Advance Preference,点击打开,把ACL Back China URL文本框里的内容改为:

https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/backcn-banAD.acl

这个方法的缺点是,你无法自定义规则。经测试,使用这份规则后,部分服务(如网易云音乐)仍然有地区限制的提示。

方法二:使用自定义PAC规则

这个方法可以自定义所有的规则,如果觉得缺少了哪条,可以自行在规则文件里补充相应的域名。

以 Mac OS X 为例,运行客户端并连接服务器,点击小飞机图标,选择PAC自动代理模式。

访问路径 ~/.ShadowsocksX-NG/gfwlist.js

在弹出的文件夹中,将gfwlist.js文件备份,然后编辑,用以下的内容替换gfwlist.js中的内容

这里,我整理了针对国内部分网站和服务的PAC列表:

更新版本可点此下载

var rules = [
//国内二级域名
  "||edu.cn",
  "||org.cn",
  "||gov.cn",
//网易
  "||163.com",
  "||126.com",
  "||127.net",
  "||netease.com",
  "||163yun.com",
  "||126.net",
//腾讯
  "||qq.com",
  "||soso.com",
  "||qpic.cn",
  "||qtimg.cn",
  "||weixin.com",
  "||qlogo.cn",
//新浪
  "||weibo.com",
  "||sinajs.cn",
  "||sina.com",
  "||sina.cn",
  "||sina.com.cn",
  "||sinaimg.cn",
  "||sinajs.cn",
  "||miaopai.com",
  "||xiaoka.tv",
  "||t.cn",
//阿里
  "||aliyun.com",
  "||taobao.com",
  "||alicdn.com",
  "||tmall.com",
  "||mmstat.com",
  "||tbcdn.com",
  "||alibabacloud.com",
  "||alipay.com",
  "||hichina.com",
  "||xiami.com",
  "||xiami.net",
//百度
  "||baidu.com",
  "||bdstatic.com",
  "||bcebos.com",
  "||baidupcs.com",
//Bilibili
  "||hdslb.com",
  "||bilibili.com",
  "||geetest.com",
//国内其它
  "||zhihu.com",
  "||zhimg.com",
  "||xiumi.us",
  "||douban.com",
  "||doubanio.com",
  "||tianyancha.com",
  "||qiniu.com"
];1

MAC:

实现步骤:

  1. 笔者的环境为Mac OS;
  2. 运行客户端并连接服务器;
  3. 选择自动代理模式;
  4. 点击运行栏上Shadowsocks的icon,选择“编辑自动模式的PAC”,或者直接访问路径 ~/.ShadowsocksX-NG/gfwlist.js
  5. 在弹出的文件夹中,将gfwlist.js文件备份,然后编辑;
  6. 用以下的内容替换gfwlist.js中的内容:
var domains = {
"tudou.com": 1,
"youku.com": 1,
"edu.cn": 1,
"163.com": 1,
"126.com": 1,
"127.net": 1,
"netease.com": 1,
"qq.com": 1,
"soso.com": 1,
"weibo.com": 1,
"zhihu.com": 1,
"sinaimg.cn": 1,
"org.cn": 1,
"gov.cn": 1,
"sh.cn": 1,
"xiumi.us": 1,
"aliyun.com": 1,
"bilibili.tv": 1,
"bilibili.com": 1,
"kankanews.com": 1,
"hdslb.com": 1,
"baidu.com": 1
};
var proxy = "SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT;";
var direct = ‘DIRECT;’;
function FindProxyForURL(url, host) {
var lastPos;
do {
if (domains.hasOwnProperty(host)) {
return proxy;
}
lastPos = host.indexOf(‘.’) + 1;
host = host.slice(lastPos);
} while (lastPos >= 1);
return direct;
}
  1. 如果要添加或替换网址(如:bilibili.com),用以下格式插入:
"gov.cn": 1,

PAC.JS

/*
翻墙回国 PAC 规则片段
---------------------------------------------------
最后更新:2019-12-26
---------------------------------------------------
 */

var proxy = "SOCKS5 127.0.0.1:1086; SOCKS 127.0.0.1:1086; DIRECT;";

var rules = [

# 国内二级域名
  "||edu.cn",
  "||gov.cn",
# AcFun
  "||acfun.cn",
  "||acfun.com",
  "||aixifan.com",
# 网易
  "||163.com",
  "||126.com",
  "||127.net",
  "||netease.com",
  "||163yun.com",
  "||126.net",
# 腾讯
  "||qq.com",
  "||soso.com",
  "||qpic.cn",
  "||gtimg.cn",
  "||weixin.com",
  "||qlogo.cn",
# 新浪
  "||weibo.com",
  "||sinajs.cn",
  "||sina.com",
  "||sina.cn",
  "||sina.com.cn",
  "||sinaimg.cn",
  "||sinajs.cn",
  "||miaopai.com",
  "||xiaoka.tv",
  "||t.cn",
# 阿里
  "||aliyun.com",
  "||taobao.com",
  "||alicdn.com",
  "||tmall.com",
  "||mmstat.com",
  "||tbcdn.com",
  "||alibabacloud.com",
  "||alipay.com",
  "||hichina.com",
  "||xiami.com",
  "||xiami.net",
# 百度
  "||baidu.com",
  "||bdstatic.com",
  "||bcebos.com",
  "||baidupcs.com",
  "||qianqian.com",
# Bilibili
  "||hdslb.com",
  "||bilibili.com",
  "||geetest.com",
  "||acgvideo.com",
# 湖南TV
  "||hitv.com",
  "||hunantv.com",
  "||mgtv.com",
# Migu
  "||cmvideo.cn",
  "||migu.cn",
  "||miguvideo.com",
# 优酷
  "||soku.com",
  "||youku.com",
  "||ykimg.com",
# 爱奇艺
  "||iqiyi.com",
  "||iqiyipic.com",
  "||qy.net",
# 搜狐
  "||sohu.com",
  "||sohu.com.cn",
  "||itc.cn",
  "||v-56.com",
# Kugou, Kuwo
  "||kugou.com",
  "||kuwo.cn",
  "||koowo.com",
# 知乎
  "||zhihu.com",
  "||zhimg.com",
# 秀米
  "||xiumi.us",
# 豆瓣
  "||douban.com",
  "||doubanio.com",
# 天眼查
  "||tianyancha.com",

# IP地址测试
  "||ip138.com"
];


/*
 * This file is part of Adblock Plus <http://adblockplus.org/>,
 * Copyright (C) 2006-2014 Eyeo GmbH
 *
 * Adblock Plus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * Adblock Plus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>.
 */

function createDict()
{
  var result = {};
  result.__proto__ = null;
  return result;
}

function getOwnPropertyDescriptor(obj, key)
{
  if (obj.hasOwnProperty(key))
  {
    return obj[key];
  }
  return null;
}

function extend(subclass, superclass, definition)
{
  if (Object.__proto__)
  {
    definition.__proto__ = superclass.prototype;
    subclass.prototype = definition;
  }
  else
  {
    var tmpclass = function(){}, ret;
    tmpclass.prototype = superclass.prototype;
    subclass.prototype = new tmpclass();
    subclass.prototype.constructor = superclass;
    for (var i in definition)
    {
      if (definition.hasOwnProperty(i))
      {
        subclass.prototype[i] = definition[i];
      }
    }
  }
}

function Filter(text)
{
  this.text = text;
  this.subscriptions = [];
}
Filter.prototype = {
text: null,
subscriptions: null,
toString: function()
  {
    return this.text;
  }
};
Filter.knownFilters = createDict();
Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/;
Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/;
Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/;
Filter.fromText = function(text)
{
  if (text in Filter.knownFilters)
  {
    return Filter.knownFilters[text];
  }
  var ret;
  if (text[0] == "!")
  {
    ret = new CommentFilter(text);
  }
  else
  {
    ret = RegExpFilter.fromText(text);
  }
  Filter.knownFilters[ret.text] = ret;
  return ret;
};

function InvalidFilter(text, reason)
{
  Filter.call(this, text);
  this.reason = reason;
}
extend(InvalidFilter, Filter, {
       reason: null
       });

function CommentFilter(text)
{
  Filter.call(this, text);
}
extend(CommentFilter, Filter, {
       });

function ActiveFilter(text, domains)
{
  Filter.call(this, text);
  this.domainSource = domains;
}
extend(ActiveFilter, Filter, {
       domainSource: null,
       domainSeparator: null,
       ignoreTrailingDot: true,
       domainSourceIsUpperCase: false,
       getDomains: function()
       {
       var prop = getOwnPropertyDescriptor(this, "domains");
       if (prop)
       {
       return prop;
       }
       var domains = null;
       if (this.domainSource)
       {
       var source = this.domainSource;
       if (!this.domainSourceIsUpperCase)
       {
       source = source.toUpperCase();
       }
       var list = source.split(this.domainSeparator);
       if (list.length == 1 && list[0][0] != "~")
       {
       domains = createDict();
       domains[""] = false;
       if (this.ignoreTrailingDot)
       {
       list[0] = list[0].replace(/\.+$/, "");
       }
       domains[list[0]] = true;
       }
       else
       {
       var hasIncludes = false;
       for (var i = 0; i < list.length; i++)
       {
       var domain = list[i];
       if (this.ignoreTrailingDot)
       {
       domain = domain.replace(/\.+$/, "");
       }
       if (domain == "")
       {
       continue;
       }
       var include;
       if (domain[0] == "~")
       {
       include = false;
       domain = domain.substr(1);
       }
       else
       {
       include = true;
       hasIncludes = true;
       }
       if (!domains)
       {
       domains = createDict();
       }
       domains[domain] = include;
       }
       domains[""] = !hasIncludes;
       }
       this.domainSource = null;
       }
       return this.domains;
       },
       sitekeys: null,
       isActiveOnDomain: function(docDomain, sitekey)
       {
       if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0))
       {
       return false;
       }
       if (!this.getDomains())
       {
       return true;
       }
       if (!docDomain)
       {
       return this.getDomains()[""];
       }
       if (this.ignoreTrailingDot)
       {
       docDomain = docDomain.replace(/\.+$/, "");
       }
       docDomain = docDomain.toUpperCase();
       while (true)
       {
       if (docDomain in this.getDomains())
       {
       return this.domains[docDomain];
       }
       var nextDot = docDomain.indexOf(".");
       if (nextDot < 0)
       {
       break;
       }
       docDomain = docDomain.substr(nextDot + 1);
       }
       return this.domains[""];
       },
       isActiveOnlyOnDomain: function(docDomain)
       {
       if (!docDomain || !this.getDomains() || this.getDomains()[""])
       {
       return false;
       }
       if (this.ignoreTrailingDot)
       {
       docDomain = docDomain.replace(/\.+$/, "");
       }
       docDomain = docDomain.toUpperCase();
       for (var domain in this.getDomains())
       {
       if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1))
       {
       return false;
       }
       }
       return true;
       }
       });

function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys)
{
  ActiveFilter.call(this, text, domains, sitekeys);
  if (contentType != null)
  {
    this.contentType = contentType;
  }
  if (matchCase)
  {
    this.matchCase = matchCase;
  }
  if (thirdParty != null)
  {
    this.thirdParty = thirdParty;
  }
  if (sitekeys != null)
  {
    this.sitekeySource = sitekeys;
  }
  if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/")
  {
    var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i");
    this.regexp = regexp;
  }
  else
  {
    this.regexpSource = regexpSource;
  }
}
extend(RegExpFilter, ActiveFilter, {
       domainSourceIsUpperCase: true,
       length: 1,
       domainSeparator: "|",
       regexpSource: null,
       getRegexp: function()
       {
       var prop = getOwnPropertyDescriptor(this, "regexp");
       if (prop)
       {
       return prop;
       }
       var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, "");
       var regexp = new RegExp(source, this.matchCase ? "" : "i");
       this.regexp = regexp;
       return regexp;
       },
       contentType: 2147483647,
       matchCase: false,
       thirdParty: null,
       sitekeySource: null,
       getSitekeys: function()
       {
       var prop = getOwnPropertyDescriptor(this, "sitekeys");
       if (prop)
       {
       return prop;
       }
       var sitekeys = null;
       if (this.sitekeySource)
       {
       sitekeys = this.sitekeySource.split("|");
       this.sitekeySource = null;
       }
       this.sitekeys = sitekeys;
       return this.sitekeys;
       },
       matches: function(location, contentType, docDomain, thirdParty, sitekey)
       {
       if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey))
       {
       return true;
       }
       return false;
       }
       });
RegExpFilter.prototype["0"] = "#this";
RegExpFilter.fromText = function(text)
{
  var blocking = true;
  var origText = text;
  if (text.indexOf("@@") == 0)
  {
    blocking = false;
    text = text.substr(2);
  }
  var contentType = null;
  var matchCase = null;
  var domains = null;
  var sitekeys = null;
  var thirdParty = null;
  var collapse = null;
  var options;
  var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null;
  if (match)
  {
    options = match[1].toUpperCase().split(",");
    text = match.input.substr(0, match.index);
    for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6)
    {
      var option = options[_loopIndex6];
      var value = null;
      var separatorIndex = option.indexOf("=");
      if (separatorIndex >= 0)
      {
        value = option.substr(separatorIndex + 1);
        option = option.substr(0, separatorIndex);
      }
      option = option.replace(/-/, "_");
      if (option in RegExpFilter.typeMap)
      {
        if (contentType == null)
        {
          contentType = 0;
        }
        contentType |= RegExpFilter.typeMap[option];
      }
      else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap)
      {
        if (contentType == null)
        {
          contentType = RegExpFilter.prototype.contentType;
        }
        contentType &= ~RegExpFilter.typeMap[option.substr(1)];
      }
      else if (option == "MATCH_CASE")
      {
        matchCase = true;
      }
      else if (option == "~MATCH_CASE")
      {
        matchCase = false;
      }
      else if (option == "DOMAIN" && typeof value != "undefined")
      {
        domains = value;
      }
      else if (option == "THIRD_PARTY")
      {
        thirdParty = true;
      }
      else if (option == "~THIRD_PARTY")
      {
        thirdParty = false;
      }
      else if (option == "COLLAPSE")
      {
        collapse = true;
      }
      else if (option == "~COLLAPSE")
      {
        collapse = false;
      }
      else if (option == "SITEKEY" && typeof value != "undefined")
      {
        sitekeys = value;
      }
      else
      {
        return new InvalidFilter(origText, "Unknown option " + option.toLowerCase());
      }
    }
  }
  if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text))
  {
    if (contentType == null)
    {
      contentType = RegExpFilter.prototype.contentType;
    }
    contentType &= ~RegExpFilter.typeMap.DOCUMENT;
  }
  try
  {
    if (blocking)
    {
      return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
    }
    else
    {
      return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys);
    }
  }
  catch (e)
  {
    return new InvalidFilter(origText, e);
  }
};
RegExpFilter.typeMap = {
OTHER: 1,
SCRIPT: 2,
IMAGE: 4,
STYLESHEET: 8,
OBJECT: 16,
SUBDOCUMENT: 32,
DOCUMENT: 64,
XBL: 1,
PING: 1,
XMLHTTPREQUEST: 2048,
OBJECT_SUBREQUEST: 4096,
DTD: 1,
MEDIA: 16384,
FONT: 32768,
BACKGROUND: 4,
POPUP: 268435456,
ELEMHIDE: 1073741824
};
RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP);

function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse)
{
  RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
  this.collapse = collapse;
}
extend(BlockingFilter, RegExpFilter, {
       collapse: null
       });

function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys)
{
  RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
}
extend(WhitelistFilter, RegExpFilter, {
       });

function Matcher()
{
  this.clear();
}
Matcher.prototype = {
filterByKeyword: null,
keywordByFilter: null,
clear: function()
  {
    this.filterByKeyword = createDict();
    this.keywordByFilter = createDict();
  },
add: function(filter)
  {
    if (filter.text in this.keywordByFilter)
    {
      return;
    }
    var keyword = this.findKeyword(filter);
    var oldEntry = this.filterByKeyword[keyword];
    if (typeof oldEntry == "undefined")
    {
      this.filterByKeyword[keyword] = filter;
    }
    else if (oldEntry.length == 1)
    {
      this.filterByKeyword[keyword] = [oldEntry, filter];
    }
    else
    {
      oldEntry.push(filter);
    }
    this.keywordByFilter[filter.text] = keyword;
  },
remove: function(filter)
  {
    if (!(filter.text in this.keywordByFilter))
    {
      return;
    }
    var keyword = this.keywordByFilter[filter.text];
    var list = this.filterByKeyword[keyword];
    if (list.length <= 1)
    {
      delete this.filterByKeyword[keyword];
    }
    else
    {
      var index = list.indexOf(filter);
      if (index >= 0)
      {
        list.splice(index, 1);
        if (list.length == 1)
        {
          this.filterByKeyword[keyword] = list[0];
        }
      }
    }
    delete this.keywordByFilter[filter.text];
  },
findKeyword: function(filter)
  {
    var result = "";
    var text = filter.text;
    if (Filter.regexpRegExp.test(text))
    {
      return result;
    }
    var match = Filter.optionsRegExp.exec(text);
    if (match)
    {
      text = match.input.substr(0, match.index);
    }
    if (text.substr(0, 2) == "@@")
    {
      text = text.substr(2);
    }
    var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g);
    if (!candidates)
    {
      return result;
    }
    var hash = this.filterByKeyword;
    var resultCount = 16777215;
    var resultLength = 0;
    for (var i = 0, l = candidates.length; i < l; i++)
    {
      var candidate = candidates[i].substr(1);
      var count = candidate in hash ? hash[candidate].length : 0;
      if (count < resultCount || count == resultCount && candidate.length > resultLength)
      {
        result = candidate;
        resultCount = count;
        resultLength = candidate.length;
      }
    }
    return result;
  },
hasFilter: function(filter)
  {
    return filter.text in this.keywordByFilter;
  },
getKeywordForFilter: function(filter)
  {
    if (filter.text in this.keywordByFilter)
    {
      return this.keywordByFilter[filter.text];
    }
    else
    {
      return null;
    }
  },
_checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey)
  {
    var list = this.filterByKeyword[keyword];
    for (var i = 0; i < list.length; i++)
    {
      var filter = list[i];
      if (filter == "#this")
      {
        filter = list;
      }
      if (filter.matches(location, contentType, docDomain, thirdParty, sitekey))
      {
        return filter;
      }
    }
    return null;
  },
matchesAny: function(location, contentType, docDomain, thirdParty, sitekey)
  {
    var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
    if (candidates === null)
    {
      candidates = [];
    }
    candidates.push("");
    for (var i = 0, l = candidates.length; i < l; i++)
    {
      var substr = candidates[i];
      if (substr in this.filterByKeyword)
      {
        var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
        if (result)
        {
          return result;
        }
      }
    }
    return null;
  }
};

function CombinedMatcher()
{
  this.blacklist = new Matcher();
  this.whitelist = new Matcher();
  this.resultCache = createDict();
}
CombinedMatcher.maxCacheEntries = 1000;
CombinedMatcher.prototype = {
blacklist: null,
whitelist: null,
resultCache: null,
cacheEntries: 0,
clear: function()
  {
    this.blacklist.clear();
    this.whitelist.clear();
    this.resultCache = createDict();
    this.cacheEntries = 0;
  },
add: function(filter)
  {
    if (filter instanceof WhitelistFilter)
    {
      this.whitelist.add(filter);
    }
    else
    {
      this.blacklist.add(filter);
    }
    if (this.cacheEntries > 0)
    {
      this.resultCache = createDict();
      this.cacheEntries = 0;
    }
  },
remove: function(filter)
  {
    if (filter instanceof WhitelistFilter)
    {
      this.whitelist.remove(filter);
    }
    else
    {
      this.blacklist.remove(filter);
    }
    if (this.cacheEntries > 0)
    {
      this.resultCache = createDict();
      this.cacheEntries = 0;
    }
  },
findKeyword: function(filter)
  {
    if (filter instanceof WhitelistFilter)
    {
      return this.whitelist.findKeyword(filter);
    }
    else
    {
      return this.blacklist.findKeyword(filter);
    }
  },
hasFilter: function(filter)
  {
    if (filter instanceof WhitelistFilter)
    {
      return this.whitelist.hasFilter(filter);
    }
    else
    {
      return this.blacklist.hasFilter(filter);
    }
  },
getKeywordForFilter: function(filter)
  {
    if (filter instanceof WhitelistFilter)
    {
      return this.whitelist.getKeywordForFilter(filter);
    }
    else
    {
      return this.blacklist.getKeywordForFilter(filter);
    }
  },
isSlowFilter: function(filter)
  {
    var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist;
    if (matcher.hasFilter(filter))
    {
      return !matcher.getKeywordForFilter(filter);
    }
    else
    {
      return !matcher.findKeyword(filter);
    }
  },
matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey)
  {
    var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
    if (candidates === null)
    {
      candidates = [];
    }
    candidates.push("");
    var blacklistHit = null;
    for (var i = 0, l = candidates.length; i < l; i++)
    {
      var substr = candidates[i];
      if (substr in this.whitelist.filterByKeyword)
      {
        var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
        if (result)
        {
          return result;
        }
      }
      if (substr in this.blacklist.filterByKeyword && blacklistHit === null)
      {
        blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
      }
    }
    return blacklistHit;
  },
matchesAny: function(location, docDomain)
  {
    var key = location + " " + docDomain + " ";
    if (key in this.resultCache)
    {
      return this.resultCache[key];
    }
    var result = this.matchesAnyInternal(location, 0, docDomain, null, null);
    if (this.cacheEntries >= CombinedMatcher.maxCacheEntries)
    {
      this.resultCache = createDict();
      this.cacheEntries = 0;
    }
    this.resultCache[key] = result;
    this.cacheEntries++;
    return result;
  }
};
var defaultMatcher = new CombinedMatcher();

var direct = 'DIRECT;';

for (var i = 0; i < rules.length; i++) {
  defaultMatcher.add(Filter.fromText(rules[i]));
}

function FindProxyForURL(url, host) {
  if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) {
    return proxy;
  }
  return direct;
}
01-07 01:14