├── .gitignore ├── LICENSE ├── README.md ├── blacklist ├── __init__.py ├── ban.py ├── builtin.py ├── custom.py ├── gfwcustom.py └── tld.py ├── data ├── cn_ip_range.txt ├── proxy.pac ├── tld.txt ├── whiteblack.pac ├── whiteiplist.pac └── whitelist.pac ├── gfwlist.txt ├── img ├── chrome-extension.png ├── chrome-pac.png ├── chrome-pac1.png ├── chrome-pac2.png ├── chrome-pac3.png └── firefox-foxyproxy.jpg ├── list_black.py ├── list_gfw.py ├── list_ip.py ├── list_white.py ├── lists ├── __init__.py ├── auto.py ├── custom.py └── pop.py ├── main.py ├── mainproxy.py ├── pactest.py ├── proxy.pac ├── ss_gfw.pac ├── ssr ├── ss_bypass.action ├── ss_cnip.pac ├── ss_gfw.pac ├── ss_lanip.pac ├── ss_r_white.pac └── ss_white.pac ├── test.html ├── test.js ├── whiteiplist.pac └── whitelist.pac /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | bin/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # Installer logs 26 | pip-log.txt 27 | pip-delete-this-directory.txt 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .cache 34 | nosetests.xml 35 | coverage.xml 36 | 37 | # Translations 38 | *.mo 39 | 40 | # Mr Developer 41 | .mr.developer.cfg 42 | .project 43 | .pydevproject 44 | 45 | # Rope 46 | .ropeproject 47 | 48 | # Django stuff: 49 | *.log 50 | *.pot 51 | 52 | # Sphinx documentation 53 | docs/_build/ 54 | 55 | # Temporary file 56 | *.swp 57 | *.tmp 58 | *.bak 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 breakwa11 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GFW 白名单 2 | 3 | 4 | 概述 5 | ----- 6 | 7 | 著名的 [autoproxy.pac](https://autoproxy.org) (GFW List) 是一个 GFW 黑名单,访问名单中网站需要通过代理,不在名单中的网站直接访问。有效使用黑名单,维护者和用户都需要时常更新此名单,否则可能不能访问最近被墙的网站。这些不便之处是推广科学上网的阻碍之一。 8 | 9 | 白名单的方法是白名单中的网站不走代理,其它网站全部通过代理访问。白名单的优点是对维护的要求非常低。第一次安装后,即使很长时间不更新,也不会出现网站打不开的问题。当然,用户会要付出稍多一些流量。 10 | 11 | 事实上 GFW 的变化速度相当快,屏蔽的网站也越来越多,国外稍微有点意思的网站大都已经被墙,或者随时可能被墙。因此作者认为有必要开始维护一份白名单的 pac 文件。 12 | 13 | 本项目包含域名白名单与IP白名单,而最终生成的实用版本列表proxy.pac同时使用黑白名单,以便于更精确更高速地科学上网。推荐直接使用proxy.pac。 14 | 15 | 本列表的国内域名部分会由作者编写的Spider自动更新,提取出常用的大部分网站。如果需要其它格式的列表请联系作者。 16 | 17 | 简单使用方法 18 | --------- 19 | 20 | ### 方法一: 21 | 下载 whitelist.pac或whiteiplist.pac或proxy.pac 文件后,修改代理服务器的 ip 地址和代理类型。然后将浏览器的代理设置中指向 whitelist.pac或whiteiplist.pac。 22 | 23 | var wall_proxy = 'PROXY www.abc.com:443;'; 24 | 以上需要更换成有效的代理地址,代理类型还可以为'SOCKS5'或'HTTPS' 25 | 多个代理之间使用分号分隔,如'PROXY a.com:80;SOCKS5 a.com:1080;' 26 | 27 | 当 `proxy` 的代理类型为 `HTTPS` 时,此 pac 文件适合用于 [Google Chrome 的安全代理](http://www.chromium.org/developers/design-documents/secure-web-proxy)。 28 | 29 | 对于不需要翻墙,但也需要代理的网站,可自行修改`nowall_proxy`,方式与`wall_proxy`相同。 30 | 31 | ### 方法二: 32 | 设置你的动态代理地址为:[http://proxy.breakwa11.ga/?proxy=SOCKS5_127.0.0.1:1080](http://proxy.breakwa11.ga/?proxy=SOCKS5_127.0.0.1:1080) 33 | 如果你所在的地区使用IP匹配不合适,那么还可以使用这个地址:[http://proxy.breakwa11.ga/?style=noip&proxy=SOCKS5_127.0.0.1:1080](http://proxy.breakwa11.ga/?style=noip&proxy=SOCKS5_127.0.0.1:1080) 34 | 注意proxy参数可修改为你自己本地的代理配置,这样即可下载一个适合你本地配置的pac文件。 35 | 参数除了proxy,还支持:nowall, auto, ip, direct,分别对应:非翻墙代理,自动代理(默认与proxy一致),ip代理(默认与nowall一致),直连代理(如果没有必要请不要修改)。参数之间需加入&分隔开。参数解释: 36 | style: 列表类型。值为noip则不使用IP匹配,大多数情况下应该使用此值,除非你解决了DNS污染。 37 | proxy: 翻墙代理,GFW黑名单使用的代理 38 | nowall: 非翻墙代理,在白名单或国内IP中使用的代理 39 | direct: 直连代理,内网地址段使用的代理 40 | ip: ip代理,访问方式为使用IP而不使用域名的地址使用的代理(国内视频站多数为ip直连,所以默认与nowall一致) 41 | auto: 自动代理,均不在以上黑白名单也不是IP直连的情况下使用的代理。与proxy一致即白名单制,与nowall一致即黑名单制。 42 | 43 | 本功能在测试中,地址随时可能发生变化,访问速度可能很慢或访问不正常,如不能访问请留意本项目的变更。 44 | 45 | 本地代理使用 `PROXY` 即http代理最佳,兼容性最好,可用于IE或iOS自动代理配置,s5代理可使用privoxy转换为http代理。不过如果是远程代理可能被偷窥连接内容。 46 | 47 | 48 | 代码生成使用方法 49 | --------- 50 | 51 | 执行 52 | 53 | main.py -o whitelist.pac -p "SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1080;" 54 | 55 | 所有选项均可忽略(直接不带参数执行也可),以上为默认值。至于whiteiplist.pac使用mainip.py生成。对于mainproxy.py执行: 56 | mainproxy.py -p "SOCKS5 127.0.0.1:1080;" -a "SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080" 57 | 58 | 参数 -a 指定的是不匹配黑白名单的时候所使用的代理,一般使用与 -p 相同的代理即可。 59 | 最后使用生成出来的 whitelist.pac或whiteiplist.pac或proxy.pac 即可 60 | 自定义列表可把你的域名加入到lists/custom.py里,然后重新执行生成操作 61 | 严重建议你把你的加入列表项通过mmgac001[at]gmail.com或者issues或者pull request告知我们 62 | 63 | > 注:Safari浏览器不支持SOCKS5关键字,因此请再额外添加以"SOCKS"关键字开头的代理,IP与端口号不变 64 | 65 | ### SSH/Goagent/http 代理设置 66 | 67 | 谈一点题外话,不少网友通过 SSH(Tunnelier/Entunnel) 等本地 socks5 代理或者 goagent 等本地 http 代理来翻墙。 68 | 69 | 假设 SSH 开的本地S5端口是 7070,goagent 的本地端口开在 8087,那么设置wall_proxy为: 70 | 71 | 72 | 'SOCKS5 127.0.0.1:7070; SOCKS 127.0.0.1:7070'; 73 | 或 74 | 75 | 'PROXY 127.0.0.1:8087'; 76 | 77 | 然后只需要将whitelist.pac文件所在地址,直接复制到填写自动代理配置的地方(见下文图),就可以用上这个白名单了。如果是本地文件,填写路径类似于"file:///d:/whitelist.pac"。 78 | 79 | Firefox 代理 80 | ----------- 81 | 82 | 安装插件[foxyproxy](https://addons.mozilla.org/zh-cn/firefox/addon/foxyproxy-standard/) 83 | 84 | ![使用 pac 文件](img/firefox-foxyproxy.jpg) 85 | 86 | 87 | Google Chrome 代理 88 | ----------- 89 | 90 | 安装插件SwitchyOmega 91 | 92 | ![新建pac配置](img/chrome-pac1.png) 93 | 94 | ![新建pac配置](img/chrome-pac2.png) 95 | 96 | 更新的pac地址: 97 | 98 | 99 | ![自动切换中将pac设置为default](img/chrome-pac3.png) 100 | 101 | 102 | 103 | Google Chrome 安全代理 (SSL Secure Proxy) 104 | ----------- 105 | 106 | Google Chrome 已经支持基于 https 和 SPDY 的安全代理。其原理和效果与 SSH,shadowsocks 以及 goagent 类似: 107 | 108 | * 将普通流量封装在加密通道之中,这样 GFW 就看不见流量的内容; 109 | * 域名的解析在代理服务器这端完成,所以本地不用担心域名污染的问题。配合 pac 的使用,可以享受国内 CDN 的服务。达到一次设置完全免维护; 110 | * 本地不从服务器端取得 ip,只适合浏览器内的应用,不适合 VoIP,网络游戏等应用。 111 | 112 | 优点有: 113 | 114 | * 在 PC 和 Mac 上 Chrome 已经原生支持,不需要依赖额外的客户端; 115 | * 封装的协议是 https 或 SPDY,GFW 完全没有 DPI 识别的可能,这是翻墙终极方案的一部分; 116 | * 由 Google 支持,客户端和服务器端的软件成熟并且稳定,未来更新也可靠。 117 | 118 | 现有的缺点有: 119 | 120 | * 暂时只适用于 PC 和 Mac 上的 Chrome。 Android 的客户端有待开发。iOS 客户端的可行性暂时还不清楚。 121 | 122 | ***有兴趣开发客户端的同学,可以考虑编译封装 @tatsuhiro-t 的 C 程序库 [spdylay](https://github.com/tatsuhiro-t/spdylay) 。*** 123 | 124 | ``` 125 | shrpx --client-proxy [-b ] [-f ] 126 | [OPTIONS...] [ ] 127 | ``` 128 | 129 | 注意事项 130 | ---------------- 131 | 使用whiteiplist.pac或proxy.pac时,你需要确认你已经解决DNS污染的问题。虽然代码中有对DNS污染做了一定的检测,但并不保证能解决所有运营商的DNS劫持。如果你的网络环境有较严重的DNS劫持影响到实际使用效果,那么建议使用whitelist.pac,或使用[dnscrypt](https://www.opendns.com/about/innovations/dnscrypt/)。 132 | 133 | 其它节省流量的方法 134 | ---------------- 135 | 136 | 由于白名单的流量消耗较黑名单要高一些,在浏览器中安装下面的扩展,在提高网页浏览速度的同时,也能节省不少流量。 137 | 138 | ##### 屏蔽广告: Adblock Plus + Easylist + Chinalist 139 | 140 | 在 Firefox 或 Chrome 中安装 [Adblock Plus](http://adblockplus.org/en/) (ABP) 扩展,并在 ABP 的控制面板中加入 Easylist 和 [Chinalist](http://code.google.com/p/adblock-chinalist/)。这样可以有效的过滤广告大部分网站和网页。 141 | 142 | `注意`:下载扩展和 ChinaList 的时候可能需要打开全局翻墙的 VPN 。 143 | 144 | ##### 屏蔽Flash: FlashControl 或 FlashBlock 145 | 146 | 在 Chrome 中安装 [FlashControl](https://chrome.google.com/webstore/detail/flashcontrol/mfidmkgnfgnkihnjeklbekckimkipmoe) 或在 Firefox 中安装 [FlashBlock](https://addons.mozilla.org/zh-cn/firefox/addon/flashblock/),可以达到屏蔽 Flash 的效果。需要打开 Flash,比如视频,只要在被屏蔽的 Flash 上点击一次。 147 | 148 | ![Chrome 的扩展](img/chrome-extension.png) 149 | 150 | PAC性能(100,000次重复执行) 151 | ---------------- 152 | firefox 153 | whitelist.pac 62ms 154 | whiteiplist.pac 77ms 155 | proxy.pac 160ms 156 | 157 | chrome 158 | whitelist.pac 94ms 159 | whiteiplist.pac 80ms 160 | proxy.pac 308ms 161 | 162 | IE9 163 | whitelist.pac 96ms 164 | whiteiplist.pac 160ms 165 | proxy.pac 349ms 166 | 167 | safari 168 | whitelist.pac 196ms 169 | whiteiplist.pac 155ms 170 | proxy.pac 552ms 171 | 172 | base on 173 | [n0wa11 gfw_whitelist](https://github.com/n0wa11/gfw_whitelist) 174 | [clowwindy gfwlist2pac](https://github.com/clowwindy/gfwlist2pac) 175 | [Leask Flora_Pac](https://github.com/Leask/Flora_Pac) 176 | 177 | ``` 178 | 任何意见或建议,请联系 mmgac001[at]gmail.com ,或提交issue到项目页面 ლ(╹◡╹ლ) 179 | ``` 180 | -------------------------------------------------------------------------------- /blacklist/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | -------------------------------------------------------------------------------- /blacklist/ban.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | tw 7 | hk 8 | github.com 9 | """ 10 | return set(liststr.splitlines(False)) 11 | -------------------------------------------------------------------------------- /blacklist/builtin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | google.com 7 | google.co.jp 8 | google.co.hk 9 | googleapis.com 10 | github.com 11 | wikipedia.org 12 | """ 13 | return set(liststr.splitlines(False)) 14 | -------------------------------------------------------------------------------- /blacklist/custom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | cloudfront.net 7 | googlecode.com 8 | verisign.com 9 | qpic.cn 10 | translate-tab.com 11 | layervault.com 12 | list-manage.com 13 | goagent.co 14 | goo.gl 15 | """ 16 | return set(liststr.splitlines(False)) 17 | -------------------------------------------------------------------------------- /blacklist/gfwcustom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | ||cloudfront.net 7 | ||googlecode.com 8 | ||verisign.com 9 | ||translate-tab.com 10 | ||layervault.com 11 | ||list-manage.com 12 | ||goagent.co 13 | ||goo.gl 14 | """ 15 | return set(liststr.splitlines(False)) 16 | 17 | -------------------------------------------------------------------------------- /blacklist/tld.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getfile(pathlist): 5 | for filename in pathlist: 6 | try: 7 | fileobj = open(filename, 'r') 8 | except: 9 | continue 10 | return fileobj 11 | 12 | def getlist(): 13 | fileobj = getfile(["../data/tld.txt", "data/tld.txt"]) 14 | data = set() 15 | for line in fileobj: 16 | line = line.strip('\n') 17 | if len(line) < 1 or line[0] == '/': 18 | continue 19 | if line[:2] == '*.': 20 | line = line[2:] 21 | if line[:1] == '!': 22 | line = line[1:] 23 | 24 | data.add(line) 25 | return data 26 | -------------------------------------------------------------------------------- /data/proxy.pac: -------------------------------------------------------------------------------- 1 | var wall_proxy = __PROXY__; 2 | var nowall_proxy = __NOWALL_PROXY__; 3 | var direct = __DIRECT__; 4 | var auto_proxy = __AUTO_PROXY__; // if you have something like COW proxy 5 | var ip_proxy = __IP_PROXY__; 6 | 7 | /* 8 | * Copyright (C) 2014 breakwa11 9 | * https://github.com/breakwa11/gfw_whitelist 10 | */ 11 | 12 | var white_domains = __WHITE_DOMAINS__; 13 | var black_domains = __GFWBLACK_DOMAINS__; 14 | 15 | var cnIpRange = __IP_LIST__; 16 | var cnIp16Range = __IP16_LIST__; 17 | 18 | var fakeIpRange = __FAKE_IP_LIST__; 19 | 20 | var subnetIpRangeList = [ 21 | 0,1, 22 | 167772160,184549376, //10.0.0.0/8 23 | 2886729728,2887778304, //172.16.0.0/12 24 | 3232235520,3232301056, //192.168.0.0/16 25 | 2130706432,2130706688 //127.0.0.0/24 26 | ]; 27 | 28 | var hasOwnProperty = Object.hasOwnProperty; 29 | 30 | function check_ipv4(host) { 31 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 32 | if (re_ipv4.test(host)) { 33 | return true; 34 | } 35 | } 36 | function convertAddress(ipchars) { 37 | var bytes = ipchars.split('.'); 38 | var result = (bytes[0] << 24) | 39 | (bytes[1] << 16) | 40 | (bytes[2] << 8) | 41 | (bytes[3]); 42 | return result >>> 0; 43 | } 44 | function isInSingleRange(ipRange, intIp) { 45 | if ( hasOwnProperty.call(cnIp16Range, intIp >>> 6) ) { 46 | for ( var range = 1; range < 64; range*=4 ) { 47 | var master = intIp & ~(range-1); 48 | if ( hasOwnProperty.call(ipRange, master) ) 49 | return intIp - master < ipRange[master]; 50 | } 51 | } else { 52 | for ( var range = 64; range <= 1024; range*=4 ) { 53 | var master = intIp & ~(range-1); 54 | if ( hasOwnProperty.call(ipRange, master) ) 55 | return intIp - master < ipRange[master]; 56 | } 57 | } 58 | } 59 | function isInSubnetRange(ipRange, intIp) { 60 | for ( var i = 0; i < 10; i += 2 ) { 61 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 62 | return true; 63 | } 64 | } 65 | function getProxyFromDirectIP(strIp) { 66 | var intIp = convertAddress(strIp); 67 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 68 | return direct; 69 | } 70 | return ip_proxy; 71 | } 72 | function getProxyFromIP(strIp) { 73 | var intIp = convertAddress(strIp); 74 | { 75 | var len = fakeIpRange.length; 76 | for ( var i = 0; i < len; ++i ) { 77 | if ( intIp == fakeIpRange[i] ) 78 | return wall_proxy; 79 | } 80 | } 81 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 82 | return direct; 83 | } 84 | var index = (intIp >>> 24) & 0xff; 85 | if ( isInSingleRange(cnIpRange[index], intIp) ) { 86 | return nowall_proxy; 87 | } 88 | return auto_proxy; 89 | } 90 | function isInDomains(domain_dict, host) { 91 | var suffix; 92 | var pos1 = host.lastIndexOf('.'); 93 | 94 | suffix = host.substring(pos1 + 1); 95 | if (suffix == "cn") { 96 | return true; 97 | } 98 | 99 | var domains = domain_dict[suffix]; 100 | if ( domains === undefined ) { 101 | return false; 102 | } 103 | host = host.substring(0, pos1); 104 | var pos = host.lastIndexOf('.'); 105 | 106 | while(1) { 107 | if (pos <= 0) { 108 | if (hasOwnProperty.call(domains, host)) { 109 | return true; 110 | } else { 111 | return false; 112 | } 113 | } 114 | suffix = host.substring(pos + 1); 115 | if (hasOwnProperty.call(domains, suffix)) { 116 | return true; 117 | } 118 | pos = host.lastIndexOf('.', pos - 1); 119 | } 120 | } 121 | 122 | /* 123 | * This file is part of Adblock Plus , 124 | * Copyright (C) 2006-2014 Eyeo GmbH 125 | * 126 | * Adblock Plus is free software: you can redistribute it and/or modify 127 | * it under the terms of the GNU General Public License version 3 as 128 | * published by the Free Software Foundation. 129 | * 130 | * Adblock Plus is distributed in the hope that it will be useful, 131 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 132 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 133 | * GNU General Public License for more details. 134 | * 135 | * You should have received a copy of the GNU General Public License 136 | * along with Adblock Plus. If not, see . 137 | */ 138 | 139 | function createDict() 140 | { 141 | var result = {}; 142 | result.__proto__ = null; 143 | return result; 144 | } 145 | 146 | function getOwnPropertyDescriptor(obj, key) 147 | { 148 | if (obj.hasOwnProperty(key)) 149 | { 150 | return obj[key]; 151 | } 152 | return null; 153 | } 154 | 155 | function extend(subclass, superclass, definition) 156 | { 157 | if (Object.__proto__) 158 | { 159 | definition.__proto__ = superclass.prototype; 160 | subclass.prototype = definition; 161 | } 162 | else 163 | { 164 | var tmpclass = function(){}, ret; 165 | tmpclass.prototype = superclass.prototype; 166 | subclass.prototype = new tmpclass(); 167 | subclass.prototype.constructor = superclass; 168 | for (var i in definition) 169 | { 170 | if (definition.hasOwnProperty(i)) 171 | { 172 | subclass.prototype[i] = definition[i]; 173 | } 174 | } 175 | } 176 | } 177 | 178 | function Filter(text) 179 | { 180 | this.text = text; 181 | this.subscriptions = []; 182 | } 183 | Filter.prototype = { 184 | text: null, 185 | subscriptions: null, 186 | toString: function() 187 | { 188 | return this.text; 189 | } 190 | }; 191 | Filter.knownFilters = createDict(); 192 | Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; 193 | Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; 194 | Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; 195 | Filter.fromText = function(text) 196 | { 197 | if (text in Filter.knownFilters) 198 | { 199 | return Filter.knownFilters[text]; 200 | } 201 | var ret; 202 | if (text[0] == "!") 203 | { 204 | ret = new CommentFilter(text); 205 | } 206 | else 207 | { 208 | ret = RegExpFilter.fromText(text); 209 | } 210 | Filter.knownFilters[ret.text] = ret; 211 | return ret; 212 | }; 213 | 214 | function InvalidFilter(text, reason) 215 | { 216 | Filter.call(this, text); 217 | this.reason = reason; 218 | } 219 | extend(InvalidFilter, Filter, { 220 | reason: null 221 | }); 222 | 223 | function CommentFilter(text) 224 | { 225 | Filter.call(this, text); 226 | } 227 | extend(CommentFilter, Filter, { 228 | }); 229 | 230 | function ActiveFilter(text, domains) 231 | { 232 | Filter.call(this, text); 233 | this.domainSource = domains; 234 | } 235 | extend(ActiveFilter, Filter, { 236 | domainSource: null, 237 | domainSeparator: null, 238 | ignoreTrailingDot: true, 239 | domainSourceIsUpperCase: false, 240 | getDomains: function() 241 | { 242 | var prop = getOwnPropertyDescriptor(this, "domains"); 243 | if (prop) 244 | { 245 | return prop; 246 | } 247 | var domains = null; 248 | if (this.domainSource) 249 | { 250 | var source = this.domainSource; 251 | if (!this.domainSourceIsUpperCase) 252 | { 253 | source = source.toUpperCase(); 254 | } 255 | var list = source.split(this.domainSeparator); 256 | if (list.length == 1 && list[0][0] != "~") 257 | { 258 | domains = createDict(); 259 | domains[""] = false; 260 | if (this.ignoreTrailingDot) 261 | { 262 | list[0] = list[0].replace(/\.+$/, ""); 263 | } 264 | domains[list[0]] = true; 265 | } 266 | else 267 | { 268 | var hasIncludes = false; 269 | for (var i = 0; i < list.length; i++) 270 | { 271 | var domain = list[i]; 272 | if (this.ignoreTrailingDot) 273 | { 274 | domain = domain.replace(/\.+$/, ""); 275 | } 276 | if (domain == "") 277 | { 278 | continue; 279 | } 280 | var include; 281 | if (domain[0] == "~") 282 | { 283 | include = false; 284 | domain = domain.substr(1); 285 | } 286 | else 287 | { 288 | include = true; 289 | hasIncludes = true; 290 | } 291 | if (!domains) 292 | { 293 | domains = createDict(); 294 | } 295 | domains[domain] = include; 296 | } 297 | domains[""] = !hasIncludes; 298 | } 299 | this.domainSource = null; 300 | } 301 | return this.domains; 302 | }, 303 | sitekeys: null, 304 | isActiveOnDomain: function(docDomain, sitekey) 305 | { 306 | if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) 307 | { 308 | return false; 309 | } 310 | if (!this.getDomains()) 311 | { 312 | return true; 313 | } 314 | if (!docDomain) 315 | { 316 | return this.getDomains()[""]; 317 | } 318 | if (this.ignoreTrailingDot) 319 | { 320 | docDomain = docDomain.replace(/\.+$/, ""); 321 | } 322 | docDomain = docDomain.toUpperCase(); 323 | while (true) 324 | { 325 | if (docDomain in this.getDomains()) 326 | { 327 | return this.domains[docDomain]; 328 | } 329 | var nextDot = docDomain.indexOf("."); 330 | if (nextDot < 0) 331 | { 332 | break; 333 | } 334 | docDomain = docDomain.substr(nextDot + 1); 335 | } 336 | return this.domains[""]; 337 | }, 338 | isActiveOnlyOnDomain: function(docDomain) 339 | { 340 | if (!docDomain || !this.getDomains() || this.getDomains()[""]) 341 | { 342 | return false; 343 | } 344 | if (this.ignoreTrailingDot) 345 | { 346 | docDomain = docDomain.replace(/\.+$/, ""); 347 | } 348 | docDomain = docDomain.toUpperCase(); 349 | for (var domain in this.getDomains()) 350 | { 351 | if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) 352 | { 353 | return false; 354 | } 355 | } 356 | return true; 357 | } 358 | }); 359 | 360 | function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 361 | { 362 | ActiveFilter.call(this, text, domains, sitekeys); 363 | if (contentType != null) 364 | { 365 | this.contentType = contentType; 366 | } 367 | if (matchCase) 368 | { 369 | this.matchCase = matchCase; 370 | } 371 | if (thirdParty != null) 372 | { 373 | this.thirdParty = thirdParty; 374 | } 375 | if (sitekeys != null) 376 | { 377 | this.sitekeySource = sitekeys; 378 | } 379 | if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/") 380 | { 381 | var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); 382 | this.regexp = regexp; 383 | } 384 | else 385 | { 386 | this.regexpSource = regexpSource; 387 | } 388 | } 389 | extend(RegExpFilter, ActiveFilter, { 390 | domainSourceIsUpperCase: true, 391 | length: 1, 392 | domainSeparator: "|", 393 | regexpSource: null, 394 | getRegexp: function() 395 | { 396 | var prop = getOwnPropertyDescriptor(this, "regexp"); 397 | if (prop) 398 | { 399 | return prop; 400 | } 401 | 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(/(\.\*)$/, ""); 402 | var regexp = new RegExp(source, this.matchCase ? "" : "i"); 403 | this.regexp = regexp; 404 | return regexp; 405 | }, 406 | contentType: 2147483647, 407 | matchCase: false, 408 | thirdParty: null, 409 | sitekeySource: null, 410 | getSitekeys: function() 411 | { 412 | var prop = getOwnPropertyDescriptor(this, "sitekeys"); 413 | if (prop) 414 | { 415 | return prop; 416 | } 417 | var sitekeys = null; 418 | if (this.sitekeySource) 419 | { 420 | sitekeys = this.sitekeySource.split("|"); 421 | this.sitekeySource = null; 422 | } 423 | this.sitekeys = sitekeys; 424 | return this.sitekeys; 425 | }, 426 | matches: function(location, contentType, docDomain, thirdParty, sitekey) 427 | { 428 | if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) 429 | { 430 | return true; 431 | } 432 | return false; 433 | } 434 | }); 435 | RegExpFilter.prototype["0"] = "#this"; 436 | RegExpFilter.fromText = function(text) 437 | { 438 | var blocking = true; 439 | var origText = text; 440 | if (text.indexOf("@@") == 0) 441 | { 442 | blocking = false; 443 | text = text.substr(2); 444 | } 445 | var contentType = null; 446 | var matchCase = null; 447 | var domains = null; 448 | var sitekeys = null; 449 | var thirdParty = null; 450 | var collapse = null; 451 | var options; 452 | var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; 453 | if (match) 454 | { 455 | options = match[1].toUpperCase().split(","); 456 | text = match.input.substr(0, match.index); 457 | for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) 458 | { 459 | var option = options[_loopIndex6]; 460 | var value = null; 461 | var separatorIndex = option.indexOf("="); 462 | if (separatorIndex >= 0) 463 | { 464 | value = option.substr(separatorIndex + 1); 465 | option = option.substr(0, separatorIndex); 466 | } 467 | option = option.replace(/-/, "_"); 468 | if (option in RegExpFilter.typeMap) 469 | { 470 | if (contentType == null) 471 | { 472 | contentType = 0; 473 | } 474 | contentType |= RegExpFilter.typeMap[option]; 475 | } 476 | else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) 477 | { 478 | if (contentType == null) 479 | { 480 | contentType = RegExpFilter.prototype.contentType; 481 | } 482 | contentType &= ~RegExpFilter.typeMap[option.substr(1)]; 483 | } 484 | else if (option == "MATCH_CASE") 485 | { 486 | matchCase = true; 487 | } 488 | else if (option == "~MATCH_CASE") 489 | { 490 | matchCase = false; 491 | } 492 | else if (option == "DOMAIN" && typeof value != "undefined") 493 | { 494 | domains = value; 495 | } 496 | else if (option == "THIRD_PARTY") 497 | { 498 | thirdParty = true; 499 | } 500 | else if (option == "~THIRD_PARTY") 501 | { 502 | thirdParty = false; 503 | } 504 | else if (option == "COLLAPSE") 505 | { 506 | collapse = true; 507 | } 508 | else if (option == "~COLLAPSE") 509 | { 510 | collapse = false; 511 | } 512 | else if (option == "SITEKEY" && typeof value != "undefined") 513 | { 514 | sitekeys = value; 515 | } 516 | else 517 | { 518 | return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); 519 | } 520 | } 521 | } 522 | if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) 523 | { 524 | if (contentType == null) 525 | { 526 | contentType = RegExpFilter.prototype.contentType; 527 | } 528 | contentType &= ~RegExpFilter.typeMap.DOCUMENT; 529 | } 530 | try 531 | { 532 | if (blocking) 533 | { 534 | return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 535 | } 536 | else 537 | { 538 | return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); 539 | } 540 | } 541 | catch (e) 542 | { 543 | return new InvalidFilter(origText, e); 544 | } 545 | }; 546 | RegExpFilter.typeMap = { 547 | OTHER: 1, 548 | SCRIPT: 2, 549 | IMAGE: 4, 550 | STYLESHEET: 8, 551 | OBJECT: 16, 552 | SUBDOCUMENT: 32, 553 | DOCUMENT: 64, 554 | XBL: 1, 555 | PING: 1, 556 | XMLHTTPREQUEST: 2048, 557 | OBJECT_SUBREQUEST: 4096, 558 | DTD: 1, 559 | MEDIA: 16384, 560 | FONT: 32768, 561 | BACKGROUND: 4, 562 | POPUP: 268435456, 563 | ELEMHIDE: 1073741824 564 | }; 565 | RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); 566 | 567 | function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) 568 | { 569 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 570 | this.collapse = collapse; 571 | } 572 | extend(BlockingFilter, RegExpFilter, { 573 | collapse: null 574 | }); 575 | 576 | function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 577 | { 578 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 579 | } 580 | extend(WhitelistFilter, RegExpFilter, { 581 | }); 582 | 583 | function Matcher() 584 | { 585 | this.clear(); 586 | } 587 | Matcher.prototype = { 588 | filterByKeyword: null, 589 | keywordByFilter: null, 590 | clear: function() 591 | { 592 | this.filterByKeyword = createDict(); 593 | this.keywordByFilter = createDict(); 594 | }, 595 | add: function(filter) 596 | { 597 | if (filter.text in this.keywordByFilter) 598 | { 599 | return; 600 | } 601 | var keyword = this.findKeyword(filter); 602 | var oldEntry = this.filterByKeyword[keyword]; 603 | if (typeof oldEntry == "undefined") 604 | { 605 | this.filterByKeyword[keyword] = filter; 606 | } 607 | else if (oldEntry.length == 1) 608 | { 609 | this.filterByKeyword[keyword] = [oldEntry, filter]; 610 | } 611 | else 612 | { 613 | oldEntry.push(filter); 614 | } 615 | this.keywordByFilter[filter.text] = keyword; 616 | }, 617 | remove: function(filter) 618 | { 619 | if (!(filter.text in this.keywordByFilter)) 620 | { 621 | return; 622 | } 623 | var keyword = this.keywordByFilter[filter.text]; 624 | var list = this.filterByKeyword[keyword]; 625 | if (list.length <= 1) 626 | { 627 | delete this.filterByKeyword[keyword]; 628 | } 629 | else 630 | { 631 | var index = list.indexOf(filter); 632 | if (index >= 0) 633 | { 634 | list.splice(index, 1); 635 | if (list.length == 1) 636 | { 637 | this.filterByKeyword[keyword] = list[0]; 638 | } 639 | } 640 | } 641 | delete this.keywordByFilter[filter.text]; 642 | }, 643 | findKeyword: function(filter) 644 | { 645 | var result = ""; 646 | var text = filter.text; 647 | if (Filter.regexpRegExp.test(text)) 648 | { 649 | return result; 650 | } 651 | var match = Filter.optionsRegExp.exec(text); 652 | if (match) 653 | { 654 | text = match.input.substr(0, match.index); 655 | } 656 | if (text.substr(0, 2) == "@@") 657 | { 658 | text = text.substr(2); 659 | } 660 | var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); 661 | if (!candidates) 662 | { 663 | return result; 664 | } 665 | var hash = this.filterByKeyword; 666 | var resultCount = 16777215; 667 | var resultLength = 0; 668 | for (var i = 0, l = candidates.length; i < l; i++) 669 | { 670 | var candidate = candidates[i].substr(1); 671 | var count = candidate in hash ? hash[candidate].length : 0; 672 | if (count < resultCount || count == resultCount && candidate.length > resultLength) 673 | { 674 | result = candidate; 675 | resultCount = count; 676 | resultLength = candidate.length; 677 | } 678 | } 679 | return result; 680 | }, 681 | hasFilter: function(filter) 682 | { 683 | return filter.text in this.keywordByFilter; 684 | }, 685 | getKeywordForFilter: function(filter) 686 | { 687 | if (filter.text in this.keywordByFilter) 688 | { 689 | return this.keywordByFilter[filter.text]; 690 | } 691 | else 692 | { 693 | return null; 694 | } 695 | }, 696 | _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) 697 | { 698 | var list = this.filterByKeyword[keyword]; 699 | for (var i = 0; i < list.length; i++) 700 | { 701 | var filter = list[i]; 702 | if (filter == "#this") 703 | { 704 | filter = list; 705 | } 706 | if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) 707 | { 708 | return filter; 709 | } 710 | } 711 | return null; 712 | }, 713 | matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) 714 | { 715 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 716 | if (candidates === null) 717 | { 718 | candidates = []; 719 | } 720 | candidates.push(""); 721 | for (var i = 0, l = candidates.length; i < l; i++) 722 | { 723 | var substr = candidates[i]; 724 | if (substr in this.filterByKeyword) 725 | { 726 | var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 727 | if (result) 728 | { 729 | return result; 730 | } 731 | } 732 | } 733 | return null; 734 | } 735 | }; 736 | 737 | function CombinedMatcher() 738 | { 739 | this.blacklist = new Matcher(); 740 | this.whitelist = new Matcher(); 741 | this.resultCache = createDict(); 742 | } 743 | CombinedMatcher.maxCacheEntries = 1000; 744 | CombinedMatcher.prototype = { 745 | blacklist: null, 746 | whitelist: null, 747 | resultCache: null, 748 | cacheEntries: 0, 749 | clear: function() 750 | { 751 | this.blacklist.clear(); 752 | this.whitelist.clear(); 753 | this.resultCache = createDict(); 754 | this.cacheEntries = 0; 755 | }, 756 | add: function(filter) 757 | { 758 | if (filter instanceof WhitelistFilter) 759 | { 760 | this.whitelist.add(filter); 761 | } 762 | else 763 | { 764 | this.blacklist.add(filter); 765 | } 766 | if (this.cacheEntries > 0) 767 | { 768 | this.resultCache = createDict(); 769 | this.cacheEntries = 0; 770 | } 771 | }, 772 | remove: function(filter) 773 | { 774 | if (filter instanceof WhitelistFilter) 775 | { 776 | this.whitelist.remove(filter); 777 | } 778 | else 779 | { 780 | this.blacklist.remove(filter); 781 | } 782 | if (this.cacheEntries > 0) 783 | { 784 | this.resultCache = createDict(); 785 | this.cacheEntries = 0; 786 | } 787 | }, 788 | findKeyword: function(filter) 789 | { 790 | if (filter instanceof WhitelistFilter) 791 | { 792 | return this.whitelist.findKeyword(filter); 793 | } 794 | else 795 | { 796 | return this.blacklist.findKeyword(filter); 797 | } 798 | }, 799 | hasFilter: function(filter) 800 | { 801 | if (filter instanceof WhitelistFilter) 802 | { 803 | return this.whitelist.hasFilter(filter); 804 | } 805 | else 806 | { 807 | return this.blacklist.hasFilter(filter); 808 | } 809 | }, 810 | getKeywordForFilter: function(filter) 811 | { 812 | if (filter instanceof WhitelistFilter) 813 | { 814 | return this.whitelist.getKeywordForFilter(filter); 815 | } 816 | else 817 | { 818 | return this.blacklist.getKeywordForFilter(filter); 819 | } 820 | }, 821 | isSlowFilter: function(filter) 822 | { 823 | var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; 824 | if (matcher.hasFilter(filter)) 825 | { 826 | return !matcher.getKeywordForFilter(filter); 827 | } 828 | else 829 | { 830 | return !matcher.findKeyword(filter); 831 | } 832 | }, 833 | matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) 834 | { 835 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 836 | if (candidates === null) 837 | { 838 | candidates = []; 839 | } 840 | candidates.push(""); 841 | var blacklistHit = null; 842 | for (var i = 0, l = candidates.length; i < l; i++) 843 | { 844 | var substr = candidates[i]; 845 | if (substr in this.whitelist.filterByKeyword) 846 | { 847 | var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 848 | if (result) 849 | { 850 | return result; 851 | } 852 | } 853 | if (substr in this.blacklist.filterByKeyword && blacklistHit === null) 854 | { 855 | blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 856 | } 857 | } 858 | return blacklistHit; 859 | }, 860 | matchesAny: function(location, docDomain) 861 | { 862 | var key = location + " " + docDomain + " "; 863 | if (key in this.resultCache) 864 | { 865 | return this.resultCache[key]; 866 | } 867 | var result = this.matchesAnyInternal(location, 0, docDomain, null, null); 868 | if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) 869 | { 870 | this.resultCache = createDict(); 871 | this.cacheEntries = 0; 872 | } 873 | this.resultCache[key] = result; 874 | this.cacheEntries++; 875 | return result; 876 | } 877 | }; 878 | var defaultMatcher = new CombinedMatcher(); 879 | 880 | function FindProxyForURL(url, host) { 881 | if ( isPlainHostName(host) === true ) { 882 | return direct; 883 | } 884 | if ( check_ipv4(host) === true ) { 885 | return getProxyFromDirectIP(host); 886 | } 887 | if ( isInDomains(white_domains, host) === true ) { 888 | return nowall_proxy; 889 | } 890 | var filter = defaultMatcher.matchesAny(url, host); 891 | if ( filter instanceof WhitelistFilter ) { 892 | return nowall_proxy; 893 | } 894 | if ( filter instanceof BlockingFilter ) { 895 | return wall_proxy; 896 | } 897 | 898 | var strIp = dnsResolve(host); 899 | if (!strIp) { 900 | return wall_proxy; 901 | } 902 | return getProxyFromIP(strIp); 903 | } 904 | 905 | -------------------------------------------------------------------------------- /data/whiteblack.pac: -------------------------------------------------------------------------------- 1 | var wall_proxy = __PROXY__; 2 | var nowall_proxy = __NOWALL_PROXY__; 3 | var direct = __DIRECT__; 4 | var auto_proxy = __AUTO_PROXY__; // if you have something like COW proxy 5 | var ip_proxy = __IP_PROXY__; 6 | 7 | /* 8 | * Copyright (C) 2014 breakwa11 9 | * https://github.com/breakwa11/gfw_whitelist 10 | */ 11 | 12 | var white_domains = __WHITE_DOMAINS__; 13 | var black_domains = __GFWBLACK_DOMAINS__; 14 | 15 | var subnetIpRangeList = [ 16 | 0,1, 17 | 167772160,184549376, //10.0.0.0/8 18 | 2886729728,2887778304, //172.16.0.0/12 19 | 3232235520,3232301056, //192.168.0.0/16 20 | 2130706432,2130706688 //127.0.0.0/24 21 | ]; 22 | 23 | var hasOwnProperty = Object.hasOwnProperty; 24 | 25 | function check_ipv4(host) { 26 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 27 | if (re_ipv4.test(host)) { 28 | return true; 29 | } 30 | } 31 | function convertAddress(ipchars) { 32 | var bytes = ipchars.split('.'); 33 | var result = (bytes[0] << 24) | 34 | (bytes[1] << 16) | 35 | (bytes[2] << 8) | 36 | (bytes[3]); 37 | return result >>> 0; 38 | } 39 | function isInSubnetRange(ipRange, intIp) { 40 | for ( var i = 0; i < 10; i += 2 ) { 41 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 42 | return true; 43 | } 44 | } 45 | function getProxyFromDirectIP(strIp) { 46 | var intIp = convertAddress(strIp); 47 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 48 | return direct; 49 | } 50 | return ip_proxy; 51 | } 52 | function isInDomains(domain_dict, host) { 53 | var suffix; 54 | var pos1 = host.lastIndexOf('.'); 55 | 56 | suffix = host.substring(pos1 + 1); 57 | if (suffix == "cn") { 58 | return true; 59 | } 60 | 61 | var domains = domain_dict[suffix]; 62 | if ( domains === undefined ) { 63 | return false; 64 | } 65 | host = host.substring(0, pos1); 66 | var pos = host.lastIndexOf('.'); 67 | 68 | while(1) { 69 | if (pos <= 0) { 70 | if (hasOwnProperty.call(domains, host)) { 71 | return true; 72 | } else { 73 | return false; 74 | } 75 | } 76 | suffix = host.substring(pos + 1); 77 | if (hasOwnProperty.call(domains, suffix)) { 78 | return true; 79 | } 80 | pos = host.lastIndexOf('.', pos - 1); 81 | } 82 | } 83 | 84 | /* 85 | * This file is part of Adblock Plus , 86 | * Copyright (C) 2006-2014 Eyeo GmbH 87 | * 88 | * Adblock Plus is free software: you can redistribute it and/or modify 89 | * it under the terms of the GNU General Public License version 3 as 90 | * published by the Free Software Foundation. 91 | * 92 | * Adblock Plus is distributed in the hope that it will be useful, 93 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 94 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 95 | * GNU General Public License for more details. 96 | * 97 | * You should have received a copy of the GNU General Public License 98 | * along with Adblock Plus. If not, see . 99 | */ 100 | 101 | function createDict() 102 | { 103 | var result = {}; 104 | result.__proto__ = null; 105 | return result; 106 | } 107 | 108 | function getOwnPropertyDescriptor(obj, key) 109 | { 110 | if (obj.hasOwnProperty(key)) 111 | { 112 | return obj[key]; 113 | } 114 | return null; 115 | } 116 | 117 | function extend(subclass, superclass, definition) 118 | { 119 | if (Object.__proto__) 120 | { 121 | definition.__proto__ = superclass.prototype; 122 | subclass.prototype = definition; 123 | } 124 | else 125 | { 126 | var tmpclass = function(){}, ret; 127 | tmpclass.prototype = superclass.prototype; 128 | subclass.prototype = new tmpclass(); 129 | subclass.prototype.constructor = superclass; 130 | for (var i in definition) 131 | { 132 | if (definition.hasOwnProperty(i)) 133 | { 134 | subclass.prototype[i] = definition[i]; 135 | } 136 | } 137 | } 138 | } 139 | 140 | function Filter(text) 141 | { 142 | this.text = text; 143 | this.subscriptions = []; 144 | } 145 | Filter.prototype = { 146 | text: null, 147 | subscriptions: null, 148 | toString: function() 149 | { 150 | return this.text; 151 | } 152 | }; 153 | Filter.knownFilters = createDict(); 154 | Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; 155 | Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; 156 | Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; 157 | Filter.fromText = function(text) 158 | { 159 | if (text in Filter.knownFilters) 160 | { 161 | return Filter.knownFilters[text]; 162 | } 163 | var ret; 164 | if (text[0] == "!") 165 | { 166 | ret = new CommentFilter(text); 167 | } 168 | else 169 | { 170 | ret = RegExpFilter.fromText(text); 171 | } 172 | Filter.knownFilters[ret.text] = ret; 173 | return ret; 174 | }; 175 | 176 | function InvalidFilter(text, reason) 177 | { 178 | Filter.call(this, text); 179 | this.reason = reason; 180 | } 181 | extend(InvalidFilter, Filter, { 182 | reason: null 183 | }); 184 | 185 | function CommentFilter(text) 186 | { 187 | Filter.call(this, text); 188 | } 189 | extend(CommentFilter, Filter, { 190 | }); 191 | 192 | function ActiveFilter(text, domains) 193 | { 194 | Filter.call(this, text); 195 | this.domainSource = domains; 196 | } 197 | extend(ActiveFilter, Filter, { 198 | domainSource: null, 199 | domainSeparator: null, 200 | ignoreTrailingDot: true, 201 | domainSourceIsUpperCase: false, 202 | getDomains: function() 203 | { 204 | var prop = getOwnPropertyDescriptor(this, "domains"); 205 | if (prop) 206 | { 207 | return prop; 208 | } 209 | var domains = null; 210 | if (this.domainSource) 211 | { 212 | var source = this.domainSource; 213 | if (!this.domainSourceIsUpperCase) 214 | { 215 | source = source.toUpperCase(); 216 | } 217 | var list = source.split(this.domainSeparator); 218 | if (list.length == 1 && list[0][0] != "~") 219 | { 220 | domains = createDict(); 221 | domains[""] = false; 222 | if (this.ignoreTrailingDot) 223 | { 224 | list[0] = list[0].replace(/\.+$/, ""); 225 | } 226 | domains[list[0]] = true; 227 | } 228 | else 229 | { 230 | var hasIncludes = false; 231 | for (var i = 0; i < list.length; i++) 232 | { 233 | var domain = list[i]; 234 | if (this.ignoreTrailingDot) 235 | { 236 | domain = domain.replace(/\.+$/, ""); 237 | } 238 | if (domain == "") 239 | { 240 | continue; 241 | } 242 | var include; 243 | if (domain[0] == "~") 244 | { 245 | include = false; 246 | domain = domain.substr(1); 247 | } 248 | else 249 | { 250 | include = true; 251 | hasIncludes = true; 252 | } 253 | if (!domains) 254 | { 255 | domains = createDict(); 256 | } 257 | domains[domain] = include; 258 | } 259 | domains[""] = !hasIncludes; 260 | } 261 | this.domainSource = null; 262 | } 263 | return this.domains; 264 | }, 265 | sitekeys: null, 266 | isActiveOnDomain: function(docDomain, sitekey) 267 | { 268 | if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) 269 | { 270 | return false; 271 | } 272 | if (!this.getDomains()) 273 | { 274 | return true; 275 | } 276 | if (!docDomain) 277 | { 278 | return this.getDomains()[""]; 279 | } 280 | if (this.ignoreTrailingDot) 281 | { 282 | docDomain = docDomain.replace(/\.+$/, ""); 283 | } 284 | docDomain = docDomain.toUpperCase(); 285 | while (true) 286 | { 287 | if (docDomain in this.getDomains()) 288 | { 289 | return this.domains[docDomain]; 290 | } 291 | var nextDot = docDomain.indexOf("."); 292 | if (nextDot < 0) 293 | { 294 | break; 295 | } 296 | docDomain = docDomain.substr(nextDot + 1); 297 | } 298 | return this.domains[""]; 299 | }, 300 | isActiveOnlyOnDomain: function(docDomain) 301 | { 302 | if (!docDomain || !this.getDomains() || this.getDomains()[""]) 303 | { 304 | return false; 305 | } 306 | if (this.ignoreTrailingDot) 307 | { 308 | docDomain = docDomain.replace(/\.+$/, ""); 309 | } 310 | docDomain = docDomain.toUpperCase(); 311 | for (var domain in this.getDomains()) 312 | { 313 | if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) 314 | { 315 | return false; 316 | } 317 | } 318 | return true; 319 | } 320 | }); 321 | 322 | function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 323 | { 324 | ActiveFilter.call(this, text, domains, sitekeys); 325 | if (contentType != null) 326 | { 327 | this.contentType = contentType; 328 | } 329 | if (matchCase) 330 | { 331 | this.matchCase = matchCase; 332 | } 333 | if (thirdParty != null) 334 | { 335 | this.thirdParty = thirdParty; 336 | } 337 | if (sitekeys != null) 338 | { 339 | this.sitekeySource = sitekeys; 340 | } 341 | if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/") 342 | { 343 | var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); 344 | this.regexp = regexp; 345 | } 346 | else 347 | { 348 | this.regexpSource = regexpSource; 349 | } 350 | } 351 | extend(RegExpFilter, ActiveFilter, { 352 | domainSourceIsUpperCase: true, 353 | length: 1, 354 | domainSeparator: "|", 355 | regexpSource: null, 356 | getRegexp: function() 357 | { 358 | var prop = getOwnPropertyDescriptor(this, "regexp"); 359 | if (prop) 360 | { 361 | return prop; 362 | } 363 | 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(/(\.\*)$/, ""); 364 | var regexp = new RegExp(source, this.matchCase ? "" : "i"); 365 | this.regexp = regexp; 366 | return regexp; 367 | }, 368 | contentType: 2147483647, 369 | matchCase: false, 370 | thirdParty: null, 371 | sitekeySource: null, 372 | getSitekeys: function() 373 | { 374 | var prop = getOwnPropertyDescriptor(this, "sitekeys"); 375 | if (prop) 376 | { 377 | return prop; 378 | } 379 | var sitekeys = null; 380 | if (this.sitekeySource) 381 | { 382 | sitekeys = this.sitekeySource.split("|"); 383 | this.sitekeySource = null; 384 | } 385 | this.sitekeys = sitekeys; 386 | return this.sitekeys; 387 | }, 388 | matches: function(location, contentType, docDomain, thirdParty, sitekey) 389 | { 390 | if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) 391 | { 392 | return true; 393 | } 394 | return false; 395 | } 396 | }); 397 | RegExpFilter.prototype["0"] = "#this"; 398 | RegExpFilter.fromText = function(text) 399 | { 400 | var blocking = true; 401 | var origText = text; 402 | if (text.indexOf("@@") == 0) 403 | { 404 | blocking = false; 405 | text = text.substr(2); 406 | } 407 | var contentType = null; 408 | var matchCase = null; 409 | var domains = null; 410 | var sitekeys = null; 411 | var thirdParty = null; 412 | var collapse = null; 413 | var options; 414 | var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; 415 | if (match) 416 | { 417 | options = match[1].toUpperCase().split(","); 418 | text = match.input.substr(0, match.index); 419 | for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) 420 | { 421 | var option = options[_loopIndex6]; 422 | var value = null; 423 | var separatorIndex = option.indexOf("="); 424 | if (separatorIndex >= 0) 425 | { 426 | value = option.substr(separatorIndex + 1); 427 | option = option.substr(0, separatorIndex); 428 | } 429 | option = option.replace(/-/, "_"); 430 | if (option in RegExpFilter.typeMap) 431 | { 432 | if (contentType == null) 433 | { 434 | contentType = 0; 435 | } 436 | contentType |= RegExpFilter.typeMap[option]; 437 | } 438 | else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) 439 | { 440 | if (contentType == null) 441 | { 442 | contentType = RegExpFilter.prototype.contentType; 443 | } 444 | contentType &= ~RegExpFilter.typeMap[option.substr(1)]; 445 | } 446 | else if (option == "MATCH_CASE") 447 | { 448 | matchCase = true; 449 | } 450 | else if (option == "~MATCH_CASE") 451 | { 452 | matchCase = false; 453 | } 454 | else if (option == "DOMAIN" && typeof value != "undefined") 455 | { 456 | domains = value; 457 | } 458 | else if (option == "THIRD_PARTY") 459 | { 460 | thirdParty = true; 461 | } 462 | else if (option == "~THIRD_PARTY") 463 | { 464 | thirdParty = false; 465 | } 466 | else if (option == "COLLAPSE") 467 | { 468 | collapse = true; 469 | } 470 | else if (option == "~COLLAPSE") 471 | { 472 | collapse = false; 473 | } 474 | else if (option == "SITEKEY" && typeof value != "undefined") 475 | { 476 | sitekeys = value; 477 | } 478 | else 479 | { 480 | return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); 481 | } 482 | } 483 | } 484 | if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) 485 | { 486 | if (contentType == null) 487 | { 488 | contentType = RegExpFilter.prototype.contentType; 489 | } 490 | contentType &= ~RegExpFilter.typeMap.DOCUMENT; 491 | } 492 | try 493 | { 494 | if (blocking) 495 | { 496 | return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 497 | } 498 | else 499 | { 500 | return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); 501 | } 502 | } 503 | catch (e) 504 | { 505 | return new InvalidFilter(origText, e); 506 | } 507 | }; 508 | RegExpFilter.typeMap = { 509 | OTHER: 1, 510 | SCRIPT: 2, 511 | IMAGE: 4, 512 | STYLESHEET: 8, 513 | OBJECT: 16, 514 | SUBDOCUMENT: 32, 515 | DOCUMENT: 64, 516 | XBL: 1, 517 | PING: 1, 518 | XMLHTTPREQUEST: 2048, 519 | OBJECT_SUBREQUEST: 4096, 520 | DTD: 1, 521 | MEDIA: 16384, 522 | FONT: 32768, 523 | BACKGROUND: 4, 524 | POPUP: 268435456, 525 | ELEMHIDE: 1073741824 526 | }; 527 | RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); 528 | 529 | function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) 530 | { 531 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 532 | this.collapse = collapse; 533 | } 534 | extend(BlockingFilter, RegExpFilter, { 535 | collapse: null 536 | }); 537 | 538 | function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 539 | { 540 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 541 | } 542 | extend(WhitelistFilter, RegExpFilter, { 543 | }); 544 | 545 | function Matcher() 546 | { 547 | this.clear(); 548 | } 549 | Matcher.prototype = { 550 | filterByKeyword: null, 551 | keywordByFilter: null, 552 | clear: function() 553 | { 554 | this.filterByKeyword = createDict(); 555 | this.keywordByFilter = createDict(); 556 | }, 557 | add: function(filter) 558 | { 559 | if (filter.text in this.keywordByFilter) 560 | { 561 | return; 562 | } 563 | var keyword = this.findKeyword(filter); 564 | var oldEntry = this.filterByKeyword[keyword]; 565 | if (typeof oldEntry == "undefined") 566 | { 567 | this.filterByKeyword[keyword] = filter; 568 | } 569 | else if (oldEntry.length == 1) 570 | { 571 | this.filterByKeyword[keyword] = [oldEntry, filter]; 572 | } 573 | else 574 | { 575 | oldEntry.push(filter); 576 | } 577 | this.keywordByFilter[filter.text] = keyword; 578 | }, 579 | remove: function(filter) 580 | { 581 | if (!(filter.text in this.keywordByFilter)) 582 | { 583 | return; 584 | } 585 | var keyword = this.keywordByFilter[filter.text]; 586 | var list = this.filterByKeyword[keyword]; 587 | if (list.length <= 1) 588 | { 589 | delete this.filterByKeyword[keyword]; 590 | } 591 | else 592 | { 593 | var index = list.indexOf(filter); 594 | if (index >= 0) 595 | { 596 | list.splice(index, 1); 597 | if (list.length == 1) 598 | { 599 | this.filterByKeyword[keyword] = list[0]; 600 | } 601 | } 602 | } 603 | delete this.keywordByFilter[filter.text]; 604 | }, 605 | findKeyword: function(filter) 606 | { 607 | var result = ""; 608 | var text = filter.text; 609 | if (Filter.regexpRegExp.test(text)) 610 | { 611 | return result; 612 | } 613 | var match = Filter.optionsRegExp.exec(text); 614 | if (match) 615 | { 616 | text = match.input.substr(0, match.index); 617 | } 618 | if (text.substr(0, 2) == "@@") 619 | { 620 | text = text.substr(2); 621 | } 622 | var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); 623 | if (!candidates) 624 | { 625 | return result; 626 | } 627 | var hash = this.filterByKeyword; 628 | var resultCount = 16777215; 629 | var resultLength = 0; 630 | for (var i = 0, l = candidates.length; i < l; i++) 631 | { 632 | var candidate = candidates[i].substr(1); 633 | var count = candidate in hash ? hash[candidate].length : 0; 634 | if (count < resultCount || count == resultCount && candidate.length > resultLength) 635 | { 636 | result = candidate; 637 | resultCount = count; 638 | resultLength = candidate.length; 639 | } 640 | } 641 | return result; 642 | }, 643 | hasFilter: function(filter) 644 | { 645 | return filter.text in this.keywordByFilter; 646 | }, 647 | getKeywordForFilter: function(filter) 648 | { 649 | if (filter.text in this.keywordByFilter) 650 | { 651 | return this.keywordByFilter[filter.text]; 652 | } 653 | else 654 | { 655 | return null; 656 | } 657 | }, 658 | _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) 659 | { 660 | var list = this.filterByKeyword[keyword]; 661 | for (var i = 0; i < list.length; i++) 662 | { 663 | var filter = list[i]; 664 | if (filter == "#this") 665 | { 666 | filter = list; 667 | } 668 | if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) 669 | { 670 | return filter; 671 | } 672 | } 673 | return null; 674 | }, 675 | matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) 676 | { 677 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 678 | if (candidates === null) 679 | { 680 | candidates = []; 681 | } 682 | candidates.push(""); 683 | for (var i = 0, l = candidates.length; i < l; i++) 684 | { 685 | var substr = candidates[i]; 686 | if (substr in this.filterByKeyword) 687 | { 688 | var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 689 | if (result) 690 | { 691 | return result; 692 | } 693 | } 694 | } 695 | return null; 696 | } 697 | }; 698 | 699 | function CombinedMatcher() 700 | { 701 | this.blacklist = new Matcher(); 702 | this.whitelist = new Matcher(); 703 | this.resultCache = createDict(); 704 | } 705 | CombinedMatcher.maxCacheEntries = 1000; 706 | CombinedMatcher.prototype = { 707 | blacklist: null, 708 | whitelist: null, 709 | resultCache: null, 710 | cacheEntries: 0, 711 | clear: function() 712 | { 713 | this.blacklist.clear(); 714 | this.whitelist.clear(); 715 | this.resultCache = createDict(); 716 | this.cacheEntries = 0; 717 | }, 718 | add: function(filter) 719 | { 720 | if (filter instanceof WhitelistFilter) 721 | { 722 | this.whitelist.add(filter); 723 | } 724 | else 725 | { 726 | this.blacklist.add(filter); 727 | } 728 | if (this.cacheEntries > 0) 729 | { 730 | this.resultCache = createDict(); 731 | this.cacheEntries = 0; 732 | } 733 | }, 734 | remove: function(filter) 735 | { 736 | if (filter instanceof WhitelistFilter) 737 | { 738 | this.whitelist.remove(filter); 739 | } 740 | else 741 | { 742 | this.blacklist.remove(filter); 743 | } 744 | if (this.cacheEntries > 0) 745 | { 746 | this.resultCache = createDict(); 747 | this.cacheEntries = 0; 748 | } 749 | }, 750 | findKeyword: function(filter) 751 | { 752 | if (filter instanceof WhitelistFilter) 753 | { 754 | return this.whitelist.findKeyword(filter); 755 | } 756 | else 757 | { 758 | return this.blacklist.findKeyword(filter); 759 | } 760 | }, 761 | hasFilter: function(filter) 762 | { 763 | if (filter instanceof WhitelistFilter) 764 | { 765 | return this.whitelist.hasFilter(filter); 766 | } 767 | else 768 | { 769 | return this.blacklist.hasFilter(filter); 770 | } 771 | }, 772 | getKeywordForFilter: function(filter) 773 | { 774 | if (filter instanceof WhitelistFilter) 775 | { 776 | return this.whitelist.getKeywordForFilter(filter); 777 | } 778 | else 779 | { 780 | return this.blacklist.getKeywordForFilter(filter); 781 | } 782 | }, 783 | isSlowFilter: function(filter) 784 | { 785 | var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; 786 | if (matcher.hasFilter(filter)) 787 | { 788 | return !matcher.getKeywordForFilter(filter); 789 | } 790 | else 791 | { 792 | return !matcher.findKeyword(filter); 793 | } 794 | }, 795 | matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) 796 | { 797 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 798 | if (candidates === null) 799 | { 800 | candidates = []; 801 | } 802 | candidates.push(""); 803 | var blacklistHit = null; 804 | for (var i = 0, l = candidates.length; i < l; i++) 805 | { 806 | var substr = candidates[i]; 807 | if (substr in this.whitelist.filterByKeyword) 808 | { 809 | var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 810 | if (result) 811 | { 812 | return result; 813 | } 814 | } 815 | if (substr in this.blacklist.filterByKeyword && blacklistHit === null) 816 | { 817 | blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 818 | } 819 | } 820 | return blacklistHit; 821 | }, 822 | matchesAny: function(location, docDomain) 823 | { 824 | var key = location + " " + docDomain + " "; 825 | if (key in this.resultCache) 826 | { 827 | return this.resultCache[key]; 828 | } 829 | var result = this.matchesAnyInternal(location, 0, docDomain, null, null); 830 | if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) 831 | { 832 | this.resultCache = createDict(); 833 | this.cacheEntries = 0; 834 | } 835 | this.resultCache[key] = result; 836 | this.cacheEntries++; 837 | return result; 838 | } 839 | }; 840 | var defaultMatcher = new CombinedMatcher(); 841 | 842 | function FindProxyForURL(url, host) { 843 | if ( isPlainHostName(host) === true ) { 844 | return direct; 845 | } 846 | if ( check_ipv4(host) === true ) { 847 | return getProxyFromDirectIP(host); 848 | } 849 | if ( isInDomains(white_domains, host) === true ) { 850 | return nowall_proxy; 851 | } 852 | var filter = defaultMatcher.matchesAny(url, host); 853 | if ( filter instanceof WhitelistFilter ) { 854 | return nowall_proxy; 855 | } 856 | if ( filter instanceof BlockingFilter ) { 857 | return wall_proxy; 858 | } 859 | 860 | return auto_proxy; 861 | } 862 | 863 | -------------------------------------------------------------------------------- /data/whiteiplist.pac: -------------------------------------------------------------------------------- 1 | var wall_proxy = __PROXY__; 2 | var nowall_proxy = "DIRECT;"; 3 | var direct = "DIRECT;"; 4 | var ip_proxy = "DIRECT;"; 5 | 6 | /* 7 | * Copyright (C) 2014 breakwa11 8 | * https://github.com/breakwa11/gfw_whitelist 9 | */ 10 | 11 | var cnIpRange = __IP_LIST__; 12 | var cnIp16Range = __IP16_LIST__; 13 | 14 | var subnetIpRangeList = [ 15 | 0,1, 16 | 167772160,184549376, //10.0.0.0/8 17 | 2886729728,2887778304, //172.16.0.0/12 18 | 3232235520,3232301056, //192.168.0.0/16 19 | 2130706432,2130706688 //127.0.0.0/24 20 | ]; 21 | 22 | var hasOwnProperty = Object.hasOwnProperty; 23 | 24 | function check_ipv4(host) { 25 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 26 | if (re_ipv4.test(host)) { 27 | return true; 28 | } 29 | } 30 | function convertAddress(ipchars) { 31 | var bytes = ipchars.split('.'); 32 | var result = (bytes[0] << 24) | 33 | (bytes[1] << 16) | 34 | (bytes[2] << 8) | 35 | (bytes[3]); 36 | return result >>> 0; 37 | } 38 | function getProxyFromDirectIP(strIp) { 39 | var intIp = convertAddress(strIp); 40 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 41 | return direct; 42 | } 43 | return ip_proxy; 44 | } 45 | function isInSingleRange(ipRange, intIp) { 46 | if ( hasOwnProperty.call(cnIp16Range, intIp >>> 6) ) { 47 | for ( var range = 1; range < 64; range*=4 ) { 48 | var master = intIp & ~(range-1); 49 | if ( hasOwnProperty.call(ipRange, master) ) 50 | return intIp - master < ipRange[master]; 51 | } 52 | } else { 53 | for ( var range = 64; range <= 1024; range*=4 ) { 54 | var master = intIp & ~(range-1); 55 | if ( hasOwnProperty.call(ipRange, master) ) 56 | return intIp - master < ipRange[master]; 57 | } 58 | } 59 | } 60 | function isInSubnetRange(ipRange, intIp) { 61 | for ( var i = 0; i < 10; i += 2 ) { 62 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 63 | return true; 64 | } 65 | } 66 | function getProxyFromIP(strIp) { 67 | var intIp = convertAddress(strIp); 68 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 69 | return direct; 70 | } 71 | var index = (intIp >>> 24) & 0xff; 72 | if ( isInSingleRange(cnIpRange[index], intIp >>> 8) ) { 73 | return nowall_proxy; 74 | } 75 | return wall_proxy; 76 | } 77 | function FindProxyForURL(url, host) { 78 | if ( isPlainHostName(host) === true ) { 79 | return direct; 80 | } 81 | if ( check_ipv4(host) === true ) { 82 | return getProxyFromDirectIP(host); 83 | } 84 | 85 | var strIp = dnsResolve(host); 86 | if (!strIp) { 87 | return wall_proxy; 88 | } 89 | 90 | return getProxyFromIP(strIp); 91 | } 92 | 93 | -------------------------------------------------------------------------------- /data/whitelist.pac: -------------------------------------------------------------------------------- 1 | var wall_proxy = __PROXY__; 2 | var nowall_proxy = "DIRECT;"; 3 | var direct = "DIRECT;"; 4 | var ip_proxy = "DIRECT;"; 5 | 6 | /* 7 | * Copyright (C) 2014 breakwa11 8 | * https://github.com/breakwa11/gfw_whitelist 9 | */ 10 | 11 | var white_domains = __DOMAINS__; 12 | 13 | var subnetIpRangeList = [ 14 | 0,1, 15 | 167772160,184549376, //10.0.0.0/8 16 | 2886729728,2887778304, //172.16.0.0/12 17 | 3232235520,3232301056, //192.168.0.0/16 18 | 2130706432,2130706688 //127.0.0.0/24 19 | ]; 20 | 21 | var hasOwnProperty = Object.hasOwnProperty; 22 | 23 | function check_ipv4(host) { 24 | // check if the ipv4 format (TODO: ipv6) 25 | // http://home.deds.nl/~aeron/regex/ 26 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 27 | if (re_ipv4.test(host)) { 28 | // in theory, we can add chnroutes test here. 29 | // but that is probably too much an overkill. 30 | return true; 31 | } 32 | } 33 | function convertAddress(ipchars) { 34 | var bytes = ipchars.split('.'); 35 | var result = (bytes[0] << 24) | 36 | (bytes[1] << 16) | 37 | (bytes[2] << 8) | 38 | (bytes[3]); 39 | return result >>> 0; 40 | } 41 | function isInSubnetRange(ipRange, intIp) { 42 | for ( var i = 0; i < 10; i += 2 ) { 43 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 44 | return true; 45 | } 46 | } 47 | function getProxyFromDirectIP(strIp) { 48 | var intIp = convertAddress(strIp); 49 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 50 | return direct; 51 | } 52 | return ip_proxy; 53 | } 54 | function isInDomains(domain_dict, host) { 55 | var suffix; 56 | var pos1 = host.lastIndexOf('.'); 57 | 58 | suffix = host.substring(pos1 + 1); 59 | if (suffix == "cn") { 60 | return true; 61 | } 62 | 63 | var domains = domain_dict[suffix]; 64 | if ( domains === undefined ) { 65 | return false; 66 | } 67 | host = host.substring(0, pos1); 68 | var pos = host.lastIndexOf('.'); 69 | 70 | while(1) { 71 | if (pos <= 0) { 72 | if (hasOwnProperty.call(domains, host)) { 73 | return true; 74 | } else { 75 | return false; 76 | } 77 | } 78 | suffix = host.substring(pos + 1); 79 | if (hasOwnProperty.call(domains, suffix)) { 80 | return true; 81 | } 82 | pos = host.lastIndexOf('.', pos - 1); 83 | } 84 | } 85 | function FindProxyForURL(url, host) { 86 | if ( isPlainHostName(host) === true ) { 87 | return direct; 88 | } 89 | if ( check_ipv4(host) === true ) { 90 | return getProxyFromDirectIP(host); 91 | } 92 | if ( isInDomains(white_domains, host) === true ) { 93 | return nowall_proxy; 94 | } 95 | return wall_proxy; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /img/chrome-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/chrome-extension.png -------------------------------------------------------------------------------- /img/chrome-pac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/chrome-pac.png -------------------------------------------------------------------------------- /img/chrome-pac1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/chrome-pac1.png -------------------------------------------------------------------------------- /img/chrome-pac2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/chrome-pac2.png -------------------------------------------------------------------------------- /img/chrome-pac3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/chrome-pac3.png -------------------------------------------------------------------------------- /img/firefox-foxyproxy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/breakwa11/gfw_whitelist/d4090ea32d7d9fd6427f32ae5b2451d72ccc612b/img/firefox-foxyproxy.jpg -------------------------------------------------------------------------------- /list_black.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import urlparse 5 | import logging 6 | import copy 7 | from blacklist import ban 8 | from blacklist import builtin 9 | from blacklist import custom 10 | from blacklist import tld 11 | 12 | __all__ = ['main'] 13 | 14 | def decode_gfwlist(content): 15 | # decode base64 if have to 16 | try: 17 | if '.' in content: 18 | raise 19 | return content.decode('base64') 20 | except: 21 | return content 22 | 23 | 24 | def get_hostname(something): 25 | try: 26 | # quite enough for GFW 27 | if not something.startswith('http:'): 28 | something = 'http://' + something 29 | r = urlparse.urlparse(something) 30 | return r.hostname 31 | except Exception as e: 32 | logging.error(e) 33 | return None 34 | 35 | def add_domain_to_set(s, something): 36 | hostname = get_hostname(something) 37 | if hostname is not None: 38 | if hostname.startswith('.'): 39 | hostname = hostname.lstrip('.') 40 | if hostname.endswith('/'): 41 | hostname = hostname.rstrip('/') 42 | if hostname: 43 | s.add(hostname) 44 | 45 | def parse_gfwlist(content): 46 | gfwlist = content.splitlines(False) 47 | domains = builtin.getlist() 48 | for line in gfwlist: 49 | if line.find('.*') >= 0: 50 | continue 51 | elif line.find('*') >= 0: 52 | line = line.replace('*', '/') 53 | if line.startswith('!'): 54 | continue 55 | elif line.startswith('['): 56 | continue 57 | elif line.startswith('@'): 58 | # ignore white list 59 | continue 60 | elif line.startswith('||!--'): 61 | continue 62 | elif line.startswith('||'): 63 | add_domain_to_set(domains, line.lstrip('||')) 64 | elif line.startswith('|'): 65 | add_domain_to_set(domains, line.lstrip('|')) 66 | elif line.startswith('.'): 67 | add_domain_to_set(domains, line.lstrip('.')) 68 | else: 69 | add_domain_to_set(domains, line) 70 | return domains 71 | 72 | def reduce_domains(domains): 73 | # reduce 'www.google.com' to 'google.com' 74 | # remove invalid domains 75 | tlds = tld.getlist() 76 | cuss = custom.getlist() 77 | bans = ban.getlist() 78 | new_domains = set() 79 | for cus in cuss: 80 | new_domains.add(cus) 81 | for domain in domains: 82 | domain_parts = domain.split('.') 83 | last_root_domain = None 84 | for i in xrange(0, len(domain_parts)): 85 | root_domain = '.'.join(domain_parts[len(domain_parts) - i - 1:]) 86 | if i == 0: 87 | if not tlds.__contains__(root_domain): 88 | # root_domain is not a valid tld 89 | break 90 | last_root_domain = root_domain 91 | if tlds.__contains__(root_domain): 92 | continue 93 | else: 94 | break 95 | if last_root_domain is not None \ 96 | and last_root_domain not in bans \ 97 | and last_root_domain not in new_domains: 98 | same = False 99 | for cus in new_domains: 100 | if len(cus) < len(last_root_domain): 101 | if cmp(cus[::-1] + '.', last_root_domain[::-1][0:len(cus)+1]) == 0 : 102 | same = True 103 | break 104 | elif len(cus) > len(last_root_domain): 105 | if cmp(last_root_domain[::-1] + '.', cus[::-1][0:len(last_root_domain)+1]) == 0 : 106 | new_domains.remove(cus) 107 | break 108 | if not same : 109 | new_domains.add(last_root_domain) 110 | return new_domains 111 | 112 | def obfs(url): 113 | ret = '' 114 | index = 0 115 | for c in url: 116 | if c == '.' or ((index & 3) == 0 and index > 0): 117 | last = ord(ret[-1]) 118 | if last < 64: 119 | ret = "%s\\%o" % (ret[:-1], last) 120 | else: 121 | ret = "%s\\x%x" % (ret[:-1], last) 122 | ret += c 123 | index += 1 124 | return ret 125 | 126 | def obfs_list(list_result): 127 | ret = set() 128 | for item in list_result: 129 | ret.add( obfs(item) ) 130 | return ret 131 | 132 | def get_all_list(lists): 133 | all_list = lists 134 | result = list() 135 | all_list.remove('') 136 | url_dict = {} 137 | for item in all_list: 138 | parts = item.split('.') 139 | if not url_dict.has_key(parts[-1]) : 140 | url_dict[parts[-1]] = list() 141 | url_dict[parts[-1]].append( obfs('.'.join(parts[:-1])) ) 142 | 143 | key_comma = '' 144 | url_dict_keys = url_dict.keys() 145 | url_dict_keys.sort() 146 | for key in url_dict_keys: 147 | url_dict[key].sort() 148 | result.append('%s"%s":{' % (key_comma, key) ) 149 | comma = '' 150 | for item in url_dict[key]: 151 | result.append(comma + '\n"' + item + '":1') 152 | comma = ',' 153 | result.append('\n}') 154 | key_comma = ',' 155 | return result 156 | 157 | def final_list(): 158 | with open('gfwlist.txt', 'r') as f: 159 | content = f.read() 160 | content = decode_gfwlist(content) 161 | #with open('gfwlist_ogn.txt', 'w') as f: 162 | # f.write(content) 163 | domains = parse_gfwlist(content) 164 | list_result = get_all_list(reduce_domains(domains)) 165 | content = ''.join(list_result) 166 | content = '{' + content + "\n}" 167 | return content 168 | 169 | -------------------------------------------------------------------------------- /list_gfw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | import urlparse 5 | import logging 6 | import copy 7 | from blacklist import gfwcustom 8 | 9 | __all__ = ['main'] 10 | 11 | def decode_gfwlist(content): 12 | # decode base64 if have to 13 | try: 14 | if '.' in content: 15 | raise 16 | return content.decode('base64') 17 | except: 18 | return content 19 | 20 | 21 | def get_hostname(something): 22 | try: 23 | # quite enough for GFW 24 | if not something.startswith('http:'): 25 | something = 'http://' + something 26 | r = urlparse.urlparse(something) 27 | return r.hostname 28 | except Exception as e: 29 | logging.error(e) 30 | return None 31 | 32 | def parse_gfwlist(content): 33 | gfwlist = content.splitlines(False) 34 | rules = list() 35 | for line in gfwlist: 36 | if line.startswith('!'): 37 | continue 38 | elif line.startswith('['): 39 | continue 40 | elif len(line.strip(' ')) <= 0: 41 | continue 42 | else: 43 | rules.append(line) 44 | return rules 45 | 46 | def obfs(url): 47 | ret = '' 48 | index = 0 49 | for c in url: 50 | if index > 0 and ( c == '.' or (index % 7) == 3 ): 51 | last = ord(ret[-1]) 52 | if last < 64: 53 | ret = "%s\\%o" % (ret[:-1], last) 54 | else: 55 | ret = "%s\\x%x" % (ret[:-1], last) 56 | ret += c 57 | index += 1 58 | return ret 59 | 60 | def obfs_list(list_result): 61 | ret = set() 62 | for item in list_result: 63 | ret.add( obfs(item) ) 64 | return ret 65 | 66 | def get_all_list(lists): 67 | result = list() 68 | key_comma = '' 69 | for key in lists: 70 | if key.startswith("@@"): 71 | result.append('%s"%s"\n' % (key_comma, key ) ) 72 | else: 73 | result.append('%s"%s"\n' % (key_comma, obfs(key) ) ) 74 | key_comma = ',' 75 | return result 76 | 77 | def final_list(): 78 | with open('gfwlist.txt', 'r') as f: 79 | content = f.read() 80 | content = decode_gfwlist(content) 81 | #with open('gfwlist_ogn.txt', 'w') as f: 82 | # f.write(content) 83 | domains = parse_gfwlist(content) 84 | gfwlist = list(gfwcustom.getlist()) 85 | gfwlist.remove("") 86 | domains += list(gfwlist) 87 | list_result = get_all_list(domains) 88 | content = ''.join(list_result) 89 | content = '[' + content + "]" 90 | return content 91 | 92 | -------------------------------------------------------------------------------- /list_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | fakeIpList = [ 4 | # https://zh.wikipedia.org/wiki/%E5%9F%9F%E5%90%8D%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BC%93%E5%AD%98%E6%B1%A1%E6%9F%93 5 | '4.36.66.178', 6 | '8.7.198.45', 7 | '23.89.5.60', 8 | '37.61.54.158', 9 | '46.82.174.68', 10 | '49.2.123.56', 11 | '54.76.135.1', 12 | '59.24.3.173', 13 | '64.33.88.161', 14 | '64.33.99.47', 15 | '64.66.163.251', 16 | '65.104.202.252', 17 | '65.160.219.113', 18 | '66.45.252.237', 19 | '72.14.205.99', 20 | '72.14.205.104', 21 | '77.4.7.92', 22 | '78.16.49.15', 23 | '93.46.8.89', 24 | '118.5.49.6', 25 | '128.121.126.139', 26 | '159.106.121.75', 27 | '169.132.13.103', 28 | '188.5.4.96', 29 | '189.163.17.5', 30 | '192.67.198.6', 31 | '197.4.4.12', 32 | '202.106.1.2', 33 | '202.181.7.85', 34 | '203.98.7.65', 35 | '203.161.230.171', 36 | '207.12.88.98', 37 | '208.56.31.43', 38 | '209.36.73.33', 39 | '209.145.54.50', 40 | '209.220.30.174', 41 | '211.94.66.147', 42 | '213.169.251.35', 43 | '216.221.188.182', 44 | '216.234.179.13', 45 | '243.185.187.39', 46 | '249.129.46.48', 47 | '253.157.14.165', 48 | '74.125.31.113', 49 | '74.125.39.102', 50 | '74.125.39.113', 51 | '74.125.127.102', 52 | '74.125.130.47', 53 | '74.125.155.102', 54 | '209.85.229.138', 55 | '210.242.125.20', 56 | '0.0.0.0', 57 | '2.1.1.2', 58 | '4.193.80.0', 59 | '8.105.84.0', 60 | '12.87.133.0', 61 | '16.63.155.0', 62 | '20.139.56.0', 63 | '24.51.184.0', 64 | '28.121.126.139', 65 | '28.13.216.0', 66 | '46.20.126.252', 67 | '46.38.24.209', 68 | '61.54.28.6', 69 | '66.206.11.194', 70 | '74.117.57.138', 71 | '89.31.55.106', 72 | '113.11.194.190', 73 | '122.218.101.190', 74 | '123.50.49.171', 75 | '123.126.249.238', 76 | '125.230.148.48', 77 | '127.0.0.2', 78 | '173.201.216.6', 79 | '203.199.57.81', 80 | '208.109.138.55', 81 | '211.5.133.18', 82 | '211.8.69.27', 83 | '213.186.33.5', 84 | '216.139.213.144', 85 | '221.8.69.27', 86 | '243.185.187.3', 87 | '243.185.187.30' 88 | ] 89 | 90 | def ip2int(ipstr): 91 | intlist = ipstr.split('.') 92 | ret = 0 93 | for i in range(4): 94 | ret = ret * 256 + int(intlist[i]) 95 | return ret 96 | 97 | def final_list(): 98 | fileobj = open("data/cn_ip_range.txt", "r") 99 | content = '' 100 | if fileobj: 101 | list_result = [] 102 | lines_list = [line.rstrip('\n').split(' ') for line in fileobj] 103 | #list_result = [ "0x%x:%s," % (int(line[0]),int(line[1])) for line in lines_list ] 104 | #''' 105 | list_result.append('{') 106 | start_num = 0 107 | comma = '' 108 | for line in lines_list: 109 | while (int(line[0]) >> 24) > start_num: 110 | start_num += 1 111 | list_result.append('},{') 112 | comma = '' 113 | list_result.append("%s0x%x:%s" % ( comma, int(line[0])/256, int(line[1])/256 )) 114 | comma = ',' 115 | list_result.append('}') 116 | #''' 117 | content = ''.join(list_result) 118 | content = '[\n' + content + "\n]" 119 | return content 120 | 121 | def center_list(): 122 | fileobj = open("data/cn_ip_range.txt", "r") 123 | master_net = set() 124 | content = '' 125 | if fileobj: 126 | list_result = [] 127 | lines_list = [line.rstrip('\n').split(' ') for line in fileobj] 128 | for line in lines_list: 129 | if int(line[1]) < (1 << 14): 130 | master_net.add( int(int(line[0]) >> 14) ) 131 | master_net = list(master_net) 132 | master_net.sort() 133 | list_result = ['0x%x:1,' % s for s in master_net] 134 | content = ''.join(list_result) 135 | content = '{\n' + content[:-1] + "\n}" 136 | return content 137 | 138 | def fake_list(): 139 | content = '' 140 | list_result = [ "0x%x," % int(ip2int(ip)) for ip in fakeIpList ] 141 | content = ''.join(list_result) 142 | content = '[\n' + content[:-1] + "\n]" 143 | return content 144 | 145 | def main(): 146 | print center_list() 147 | 148 | if __name__ == '__main__': 149 | main() 150 | -------------------------------------------------------------------------------- /list_white.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def get_all_list(lists): 5 | all_list = set() 6 | result = list() 7 | for item in lists: 8 | all_list = all_list | item.getlist() 9 | all_list.remove('') 10 | url_dict = {} 11 | for item in all_list: 12 | parts = item.split('.') 13 | if not url_dict.has_key(parts[-1]) : 14 | url_dict[parts[-1]] = list() 15 | url_dict[parts[-1]].append( '.'.join(parts[:-1]) ) 16 | 17 | key_comma = '' 18 | url_dict_keys = url_dict.keys() 19 | url_dict_keys.sort() 20 | for key in url_dict_keys: 21 | url_dict[key].sort() 22 | result.append('%s"%s":{' % (key_comma, key) ) 23 | comma = '' 24 | for item in url_dict[key]: 25 | result.append(comma + '\n"' + item + '":1') 26 | comma = ',' 27 | result.append('\n}') 28 | key_comma = ',' 29 | return result 30 | 31 | def final_list(): 32 | import lists 33 | list_result = get_all_list(lists.get_list_set()) 34 | content = ''.join(list_result) 35 | content = '{' + content + "\n}" 36 | return content 37 | 38 | -------------------------------------------------------------------------------- /lists/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def get_list_set(): 5 | import custom 6 | import pop 7 | import auto 8 | return [custom, pop, auto] 9 | 10 | -------------------------------------------------------------------------------- /lists/custom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | """ 7 | return set(liststr.splitlines(False)) 8 | -------------------------------------------------------------------------------- /lists/pop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | def getlist(): 5 | liststr = """ 6 | 115img.com 7 | 2mdn.net 8 | 360doc.com 9 | 360buyimg.com 10 | 51buy.com 11 | acfun.com 12 | acfun.tv 13 | acg.tv 14 | acgvideo.com 15 | adnxs.com 16 | adroll.com 17 | adsame.com 18 | adsonar.com 19 | adtechus.com 20 | alipayobjects.com 21 | appgame.com 22 | atdmt.com 23 | betrad.com 24 | bilibili.com 25 | bilibili.tv 26 | bluekai.com 27 | cctv.com 28 | cnbeta.com 29 | compete.com 30 | doc88.com 31 | douban.fm 32 | doubanio.com 33 | doubleclick.net 34 | ebay.com 35 | fastcdn.com 36 | ftchinese.com 37 | gamesville.com 38 | gtimg.com 39 | harrenmedianetwork.com 40 | homeinns.com 41 | iask.com 42 | icson.com 43 | ifeng.com 44 | img.cctvpic.com 45 | imqq.com 46 | imrworldwide.com 47 | invitemedia.com 48 | ipinyou.com 49 | irs01.com 50 | irs01.net 51 | iteye.com 52 | jandan.net 53 | jing.fm 54 | jysq.net 55 | kandian.com 56 | ku6cdn.com 57 | ku6img.com 58 | kuaidi100.com 59 | kuaidi100.net 60 | lashou.com 61 | lashouimg.com 62 | legolas-media.com 63 | logmein.com 64 | lxdns.com 65 | lycos.com 66 | lygo.com 67 | mathtag.com 68 | mediaplex.com 69 | mediav.com 70 | miaozhen.com 71 | microsoft.com 72 | mlt01.com 73 | mm111.net 74 | mmstat.com 75 | mookie1.com 76 | mosso.com 77 | netease.com 78 | newsmth.net 79 | nipic.com 80 | oadz.com 81 | oeeee.com 82 | oschina.net 83 | paypal.com 84 | pengyou.com 85 | pixlr.com 86 | ppstream.com 87 | pubmatic.com 88 | qiniucdn.com 89 | qq.com 90 | qstatic.com 91 | quantserve.com 92 | renren.com 93 | rrimg.com 94 | rtbidder.net 95 | scanscout.com 96 | scorecardresearch.com 97 | serving-sys.com 98 | sina.com 99 | sinaedge.com 100 | sinaimg.com 101 | sinahk.net 102 | sinajs.com 103 | snyu.com 104 | staticsdo.com 105 | tanx.com 106 | tdimg.com 107 | tremormedia.com 108 | v2ex.com 109 | vizu.com 110 | williamlong.info 111 | wrating.com 112 | yieldmanager.com 113 | ykimg.com 114 | youshang.com 115 | zdmimg.com 116 | zhibo8.com 117 | zhimg.com 118 | zoopda.com 119 | zuoche.com 120 | """ 121 | return set(liststr.splitlines(False)) 122 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | 5 | from argparse import ArgumentParser 6 | import list_white 7 | import list_ip 8 | 9 | def parse_args(): 10 | parser = ArgumentParser() 11 | parser.add_argument('-i', '--input', dest='input', default=os.path.join('data','whitelist.pac'), 12 | help='path to gfwlist') 13 | parser.add_argument('-o', '--output', dest='output', default='whitelist.pac', 14 | help='path to output pac', metavar='PAC') 15 | parser.add_argument('-p', '--proxy', dest='proxy', default='"SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080;"', 16 | help='the proxy parameter in the pac file, for example,\ 17 | "127.0.0.1:1080;"', metavar='SOCKS5') 18 | return parser.parse_args() 19 | 20 | def get_file_data(filename): 21 | content = '' 22 | with open(filename, 'r') as file_obj: 23 | content = file_obj.read() 24 | return content 25 | 26 | def writefile(input_file, proxy, output_file): 27 | ip_content = list_ip.final_list() 28 | ip16_content = list_ip.center_list() 29 | fake_ip_content = list_ip.fake_list() 30 | domains_content = list_white.final_list() 31 | proxy_content = get_file_data(input_file) 32 | proxy_content = proxy_content.replace('__PROXY__', proxy) 33 | proxy_content = proxy_content.replace('__DOMAINS__', domains_content) 34 | proxy_content = proxy_content.replace('__IP_LIST__', ip_content) 35 | proxy_content = proxy_content.replace('__IP16_LIST__', ip16_content) 36 | proxy_content = proxy_content.replace('__FAKE_IP_LIST__', fake_ip_content) 37 | with open(output_file, 'w') as file_obj: 38 | file_obj.write(proxy_content) 39 | 40 | def main(): 41 | args = parse_args() 42 | writefile(args.input, '"' + args.proxy.strip('"') + '"', args.output) 43 | 44 | if __name__ == '__main__': 45 | main() 46 | 47 | -------------------------------------------------------------------------------- /mainproxy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from argparse import ArgumentParser 4 | import os 5 | import re 6 | import list_white 7 | import list_black 8 | import list_gfw 9 | import list_ip 10 | 11 | def parse_args(): 12 | parser = ArgumentParser() 13 | parser.add_argument('-i', '--input', dest='input', default=os.path.join('data', 'proxy.pac'), 14 | help='path to gfwlist') 15 | parser.add_argument('-o', '--output', dest='output', default='proxy.pac', 16 | help='path to output pac', metavar='PAC') 17 | parser.add_argument('-p', '--proxy', dest='proxy', default='"SOCKS5 127.0.0.1:1080;"', 18 | help='the proxy parameter in the pac file, for example,\ 19 | "SOCKS5 127.0.0.1:1080;"', metavar='SOCKS5') 20 | parser.add_argument('-a', '--auto_proxy', dest='auto_proxy', default='auto', 21 | help='the auto proxy parameter in the pac file, for example,\ 22 | "SOCKS5 127.0.0.1:1080;"', metavar='SOCKS5') 23 | parser.add_argument('-d', '--directip_proxy', dest='ip_proxy', default='auto', 24 | help='the auto proxy parameter in the pac file, for example,\ 25 | "SOCKS5 127.0.0.1:1080;"', metavar='SOCKS5') 26 | parser.add_argument('-z', '--dynamic', dest='dynamic', default='no') 27 | return parser.parse_args() 28 | 29 | def get_file_data(filename): 30 | content = '' 31 | with open(filename, 'r') as file_obj: 32 | content = file_obj.read() 33 | return content 34 | 35 | def js_shorter(content): 36 | result = [] 37 | class ParseState(object): 38 | def __init__(self): 39 | self.state = 'empty' 40 | self.buff = '' 41 | self.cur = '' 42 | self.laststate = 'empty' 43 | self.word_set = set() 44 | for i in xrange(26): 45 | self.word_set.add(chr(i + ord('A'))) 46 | self.word_set.add(chr(i + ord('a'))) 47 | for i in xrange(10): 48 | self.word_set.add(chr(i + ord('0'))) 49 | self.word_set.add('_') 50 | self.word_set.add('$') 51 | self.empty_set = set([' ', '\t', '\r', '\n']) 52 | def retbuf(self): 53 | ret = self.buff 54 | self.buff = '' 55 | return ret 56 | def next(self, ch): 57 | self.buff += self.cur 58 | self.cur = ch 59 | if self.state == 'empty': 60 | if ch in self.empty_set: 61 | self.cur = '' 62 | elif ch in self.word_set: 63 | self.state = 'word' 64 | if self.laststate == 'word': 65 | return ' ' + self.retbuf() 66 | return self.retbuf() 67 | elif ch == '/': 68 | self.state = 'slash' 69 | elif ch == '"': 70 | self.state = 'string' 71 | else: 72 | self.state = 'symbol' 73 | elif self.state == 'symbol': # symbol 74 | self.laststate = 'symbol' 75 | if ch in self.empty_set: 76 | self.cur = '' 77 | self.state = 'empty' 78 | elif ch in self.word_set: 79 | self.state = 'word' 80 | elif ch == '/': 81 | self.state = 'slash' 82 | elif ch == '"': 83 | self.state = 'string' 84 | else: 85 | self.state = 'symbol' 86 | return self.retbuf() 87 | elif self.state == 'word': 88 | self.laststate = 'word' 89 | if ch in self.empty_set: 90 | self.cur = '' 91 | self.state = 'empty' 92 | return self.retbuf() 93 | elif ch in self.word_set: 94 | self.state = 'word' 95 | elif ch == '/': 96 | self.state = 'slash' 97 | return self.retbuf() 98 | elif ch == '"': 99 | self.state = 'string' 100 | return self.retbuf() 101 | else: 102 | self.state = 'symbol' 103 | return self.retbuf() 104 | elif self.state == 'string': 105 | if ch == '"': 106 | self.cur = '' 107 | self.state = 'empty' 108 | return self.retbuf() + ch 109 | elif ch == '\\': 110 | self.state = 'string_conv' 111 | elif self.state == 'string_conv': 112 | self.state = 'string' 113 | elif self.state == 'slash': # just OK in this case 114 | if ch == '/': 115 | self.state = 'comment' 116 | elif ch == '*': 117 | self.state = 'multiline_comment' 118 | else: 119 | self.state = 'symbol' 120 | cur = self.cur 121 | self.cur = '' 122 | return self.next(ch) 123 | elif self.state == 'comment': 124 | if ch == '\n': 125 | self.buff = '' 126 | self.cur = '' 127 | self.state = 'empty' 128 | elif self.state == 'multiline_comment': 129 | if ch == '*': 130 | self.state = 'multiline_comment_ed1' 131 | elif self.state == 'multiline_comment_ed1': 132 | if ch == '/': 133 | # keep multiline_comment 134 | self.state = 'symbol' 135 | else: 136 | self.state = 'multiline_comment' 137 | return '' 138 | prase = ParseState() 139 | for c in content: 140 | r = prase.next(c) 141 | if len(r) > 0: 142 | result.append(r) 143 | return ''.join(result) 144 | 145 | def writefile(input_file, proxy, auto_proxy, ip_proxy, output_file, dynamic): 146 | ip_content = list_ip.final_list() 147 | ip16_content = list_ip.center_list() 148 | proxy_content = get_file_data(input_file) 149 | proxy_content = proxy_content.replace('__IP_LIST__', ip_content) 150 | proxy_content = proxy_content.replace('__IP16_LIST__', ip16_content) 151 | proxy_content = proxy_content.replace('__FAKE_IP_LIST__', list_ip.fake_list()) 152 | proxy_content = proxy_content.replace('__WHITE_DOMAINS__', list_white.final_list()) 153 | if '__BLACK_DOMAINS__' in proxy_content: 154 | proxy_content = proxy_content.replace('__BLACK_DOMAINS__', list_black.final_list()) 155 | if '__GFWBLACK_DOMAINS__' in proxy_content: 156 | proxy_content = proxy_content.replace('__GFWBLACK_DOMAINS__', list_gfw.final_list()) 157 | 158 | if dynamic != 'no': 159 | with open(dynamic + output_file, 'w') as file_obj: 160 | file_obj.write( js_shorter(proxy_content) ) 161 | 162 | proxy_content = proxy_content.replace('__PROXY__', proxy) 163 | proxy_content = proxy_content.replace('__NOWALL_PROXY__', '"DIRECT;"') 164 | proxy_content = proxy_content.replace('__IP_PROXY__', ip_proxy) 165 | proxy_content = proxy_content.replace('__AUTO_PROXY__', auto_proxy) 166 | proxy_content = proxy_content.replace('__DIRECT__', '"DIRECT;"') 167 | with open(output_file, 'w') as file_obj: 168 | file_obj.write(proxy_content) 169 | 170 | with open('min_' + output_file, 'w') as file_obj: 171 | file_obj.write(js_shorter(proxy_content)) 172 | 173 | def get_default_param(param, defstring, defval): 174 | if param == defstring: 175 | return defval 176 | return '"' + param.strip('"') + '"' 177 | 178 | def main(): 179 | args = parse_args() 180 | 181 | auto_proxy = get_default_param(args.auto_proxy, 'auto', 'wall_proxy') 182 | ip_proxy = get_default_param(args.ip_proxy, 'auto', 'nowall_proxy') 183 | 184 | writefile(args.input, '"' + args.proxy.strip('"') + '"', auto_proxy, ip_proxy, args.output, args.dynamic) 185 | 186 | if __name__ == '__main__': 187 | main() 188 | 189 | -------------------------------------------------------------------------------- /pactest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | #-*- coding: utf-8 -*- 3 | 4 | ''' 5 | You have to install pacparser before runing this script. 6 | You can get pacparser from https://code.google.com/p/pacparser. 7 | ''' 8 | 9 | import pacparser 10 | import time 11 | 12 | def get_pac_result(filename, url, host): 13 | pacparser.init() 14 | pacparser.parse_pac(filename) 15 | ret_str = pacparser.find_proxy(url, host) 16 | pacparser.cleanup() 17 | return ret_str 18 | 19 | def main_test(filename, test_times): 20 | pacparser.init() 21 | pacparser.parse_pac(filename) 22 | beg_time = time.time() 23 | for i in xrange(test_times): 24 | ret_str = pacparser.find_proxy('http://www.coding.com', 'www.coding.com') # using the worst case 25 | end_time = time.time() 26 | print "%s:\nTotal Time: %s s\nAvg. Time: %s ms\n\n" % (filename, end_time - beg_time, (end_time - beg_time) * 1000.0 / test_times), 27 | pacparser.cleanup() 28 | 29 | def time_test(): 30 | main_test("whitelist.pac", 10000) 31 | main_test("whiteiplist.pac", 100) 32 | #main_test("flora_pac.pac", 100) 33 | #main_test("usufu_flora_pac.pac", 100) 34 | 35 | def main(): 36 | time_test() 37 | 38 | main() 39 | -------------------------------------------------------------------------------- /ss_gfw.pac: -------------------------------------------------------------------------------- 1 | var direct = "__DIRECT__"; 2 | if (direct == "__DIR" + "ECT__") direct = "DIRECT;"; 3 | 4 | var wall_proxy = function(){ return "__PROXY__"; }; 5 | var wall_v6_proxy = function(){ return "__PROXY__"; }; 6 | 7 | var nowall_proxy = function(){ return direct; }; 8 | var ip_proxy = function(){ return nowall_proxy(); }; 9 | var ipv6_proxy = function(){ return nowall_proxy(); }; 10 | 11 | /* 12 | * Copyright (C) 2014 breakwa11 13 | * https://github.com/breakwa11/gfw_whitelist 14 | */ 15 | 16 | var rules = __RULES__; 17 | 18 | /* 19 | * This file is part of Adblock Plus , 20 | * Copyright (C) 2006-2014 Eyeo GmbH 21 | * 22 | * Adblock Plus is free software: you can redistribute it and/or modify 23 | * it under the terms of the GNU General Public License version 3 as 24 | * published by the Free Software Foundation. 25 | * 26 | * Adblock Plus is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | * GNU General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU General Public License 32 | * along with Adblock Plus. If not, see . 33 | */ 34 | 35 | function createDict() 36 | { 37 | var result = {}; 38 | result.__proto__ = null; 39 | return result; 40 | } 41 | 42 | function getOwnPropertyDescriptor(obj, key) 43 | { 44 | if (obj.hasOwnProperty(key)) 45 | { 46 | return obj[key]; 47 | } 48 | return null; 49 | } 50 | 51 | function extend(subclass, superclass, definition) 52 | { 53 | if (Object.__proto__) 54 | { 55 | definition.__proto__ = superclass.prototype; 56 | subclass.prototype = definition; 57 | } 58 | else 59 | { 60 | var tmpclass = function(){}, ret; 61 | tmpclass.prototype = superclass.prototype; 62 | subclass.prototype = new tmpclass(); 63 | subclass.prototype.constructor = superclass; 64 | for (var i in definition) 65 | { 66 | if (definition.hasOwnProperty(i)) 67 | { 68 | subclass.prototype[i] = definition[i]; 69 | } 70 | } 71 | } 72 | } 73 | 74 | function Filter(text) 75 | { 76 | this.text = text; 77 | this.subscriptions = []; 78 | } 79 | Filter.prototype = { 80 | text: null, 81 | subscriptions: null, 82 | toString: function() 83 | { 84 | return this.text; 85 | } 86 | }; 87 | Filter.knownFilters = createDict(); 88 | Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; 89 | Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; 90 | Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; 91 | Filter.fromText = function(text) 92 | { 93 | if (text in Filter.knownFilters) 94 | { 95 | return Filter.knownFilters[text]; 96 | } 97 | var ret; 98 | if (text.charAt(0) == "!") 99 | { 100 | ret = new CommentFilter(text); 101 | } 102 | else 103 | { 104 | ret = RegExpFilter.fromText(text); 105 | } 106 | Filter.knownFilters[ret.text] = ret; 107 | return ret; 108 | }; 109 | 110 | function InvalidFilter(text, reason) 111 | { 112 | Filter.call(this, text); 113 | this.reason = reason; 114 | } 115 | extend(InvalidFilter, Filter, { 116 | reason: null 117 | }); 118 | 119 | function CommentFilter(text) 120 | { 121 | Filter.call(this, text); 122 | } 123 | extend(CommentFilter, Filter, { 124 | }); 125 | 126 | function ActiveFilter(text, domains) 127 | { 128 | Filter.call(this, text); 129 | this.domainSource = domains; 130 | } 131 | extend(ActiveFilter, Filter, { 132 | domainSource: null, 133 | domainSeparator: null, 134 | ignoreTrailingDot: true, 135 | domainSourceIsUpperCase: false, 136 | getDomains: function() 137 | { 138 | var prop = getOwnPropertyDescriptor(this, "domains"); 139 | if (prop) 140 | { 141 | return prop; 142 | } 143 | var domains = null; 144 | if (this.domainSource) 145 | { 146 | var source = this.domainSource; 147 | if (!this.domainSourceIsUpperCase) 148 | { 149 | source = source.toUpperCase(); 150 | } 151 | var list = source.split(this.domainSeparator); 152 | if (list.length == 1 && (list[0]).charAt(0) != "~") 153 | { 154 | domains = createDict(); 155 | domains[""] = false; 156 | if (this.ignoreTrailingDot) 157 | { 158 | list[0] = list[0].replace(/\.+$/, ""); 159 | } 160 | domains[list[0]] = true; 161 | } 162 | else 163 | { 164 | var hasIncludes = false; 165 | for (var i = 0; i < list.length; i++) 166 | { 167 | var domain = list[i]; 168 | if (this.ignoreTrailingDot) 169 | { 170 | domain = domain.replace(/\.+$/, ""); 171 | } 172 | if (domain == "") 173 | { 174 | continue; 175 | } 176 | var include; 177 | if (domain.charAt(0) == "~") 178 | { 179 | include = false; 180 | domain = domain.substr(1); 181 | } 182 | else 183 | { 184 | include = true; 185 | hasIncludes = true; 186 | } 187 | if (!domains) 188 | { 189 | domains = createDict(); 190 | } 191 | domains[domain] = include; 192 | } 193 | domains[""] = !hasIncludes; 194 | } 195 | this.domainSource = null; 196 | } 197 | return this.domains; 198 | }, 199 | sitekeys: null, 200 | isActiveOnDomain: function(docDomain, sitekey) 201 | { 202 | if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) 203 | { 204 | return false; 205 | } 206 | if (!this.getDomains()) 207 | { 208 | return true; 209 | } 210 | if (!docDomain) 211 | { 212 | return this.getDomains()[""]; 213 | } 214 | if (this.ignoreTrailingDot) 215 | { 216 | docDomain = docDomain.replace(/\.+$/, ""); 217 | } 218 | docDomain = docDomain.toUpperCase(); 219 | while (true) 220 | { 221 | if (docDomain in this.getDomains()) 222 | { 223 | return this.domains[docDomain]; 224 | } 225 | var nextDot = docDomain.indexOf("."); 226 | if (nextDot < 0) 227 | { 228 | break; 229 | } 230 | docDomain = docDomain.substr(nextDot + 1); 231 | } 232 | return this.domains[""]; 233 | }, 234 | isActiveOnlyOnDomain: function(docDomain) 235 | { 236 | if (!docDomain || !this.getDomains() || this.getDomains()[""]) 237 | { 238 | return false; 239 | } 240 | if (this.ignoreTrailingDot) 241 | { 242 | docDomain = docDomain.replace(/\.+$/, ""); 243 | } 244 | docDomain = docDomain.toUpperCase(); 245 | for (var domain in this.getDomains()) 246 | { 247 | if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) 248 | { 249 | return false; 250 | } 251 | } 252 | return true; 253 | } 254 | }); 255 | 256 | function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 257 | { 258 | ActiveFilter.call(this, text, domains, sitekeys); 259 | if (contentType != null) 260 | { 261 | this.contentType = contentType; 262 | } 263 | if (matchCase) 264 | { 265 | this.matchCase = matchCase; 266 | } 267 | if (thirdParty != null) 268 | { 269 | this.thirdParty = thirdParty; 270 | } 271 | if (sitekeys != null) 272 | { 273 | this.sitekeySource = sitekeys; 274 | } 275 | if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") 276 | { 277 | var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); 278 | this.regexp = regexp; 279 | } 280 | else 281 | { 282 | this.regexpSource = regexpSource; 283 | } 284 | } 285 | extend(RegExpFilter, ActiveFilter, { 286 | domainSourceIsUpperCase: true, 287 | length: 1, 288 | domainSeparator: "|", 289 | regexpSource: null, 290 | getRegexp: function() 291 | { 292 | var prop = getOwnPropertyDescriptor(this, "regexp"); 293 | if (prop) 294 | { 295 | return prop; 296 | } 297 | 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(/(\.\*)$/, ""); 298 | var regexp = new RegExp(source, this.matchCase ? "" : "i"); 299 | this.regexp = regexp; 300 | return regexp; 301 | }, 302 | contentType: 2147483647, 303 | matchCase: false, 304 | thirdParty: null, 305 | sitekeySource: null, 306 | getSitekeys: function() 307 | { 308 | var prop = getOwnPropertyDescriptor(this, "sitekeys"); 309 | if (prop) 310 | { 311 | return prop; 312 | } 313 | var sitekeys = null; 314 | if (this.sitekeySource) 315 | { 316 | sitekeys = this.sitekeySource.split("|"); 317 | this.sitekeySource = null; 318 | } 319 | this.sitekeys = sitekeys; 320 | return this.sitekeys; 321 | }, 322 | matches: function(location, contentType, docDomain, thirdParty, sitekey) 323 | { 324 | if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) 325 | { 326 | return true; 327 | } 328 | return false; 329 | } 330 | }); 331 | RegExpFilter.prototype["0"] = "#this"; 332 | RegExpFilter.fromText = function(text) 333 | { 334 | var blocking = true; 335 | var origText = text; 336 | if (text.indexOf("@@") == 0) 337 | { 338 | blocking = false; 339 | text = text.substr(2); 340 | } 341 | var contentType = null; 342 | var matchCase = null; 343 | var domains = null; 344 | var sitekeys = null; 345 | var thirdParty = null; 346 | var collapse = null; 347 | var options; 348 | var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; 349 | if (match) 350 | { 351 | options = match[1].toUpperCase().split(","); 352 | text = match.input.substr(0, match.index); 353 | for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) 354 | { 355 | var option = options[_loopIndex6]; 356 | var value = null; 357 | var separatorIndex = option.indexOf("="); 358 | if (separatorIndex >= 0) 359 | { 360 | value = option.substr(separatorIndex + 1); 361 | option = option.substr(0, separatorIndex); 362 | } 363 | option = option.replace(/-/, "_"); 364 | if (option in RegExpFilter.typeMap) 365 | { 366 | if (contentType == null) 367 | { 368 | contentType = 0; 369 | } 370 | contentType |= RegExpFilter.typeMap[option]; 371 | } 372 | else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) 373 | { 374 | if (contentType == null) 375 | { 376 | contentType = RegExpFilter.prototype.contentType; 377 | } 378 | contentType &= ~RegExpFilter.typeMap[option.substr(1)]; 379 | } 380 | else if (option == "MATCH_CASE") 381 | { 382 | matchCase = true; 383 | } 384 | else if (option == "~MATCH_CASE") 385 | { 386 | matchCase = false; 387 | } 388 | else if (option == "DOMAIN" && typeof value != "undefined") 389 | { 390 | domains = value; 391 | } 392 | else if (option == "THIRD_PARTY") 393 | { 394 | thirdParty = true; 395 | } 396 | else if (option == "~THIRD_PARTY") 397 | { 398 | thirdParty = false; 399 | } 400 | else if (option == "COLLAPSE") 401 | { 402 | collapse = true; 403 | } 404 | else if (option == "~COLLAPSE") 405 | { 406 | collapse = false; 407 | } 408 | else if (option == "SITEKEY" && typeof value != "undefined") 409 | { 410 | sitekeys = value; 411 | } 412 | else 413 | { 414 | return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); 415 | } 416 | } 417 | } 418 | if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) 419 | { 420 | if (contentType == null) 421 | { 422 | contentType = RegExpFilter.prototype.contentType; 423 | } 424 | contentType &= ~RegExpFilter.typeMap.DOCUMENT; 425 | } 426 | try 427 | { 428 | if (blocking) 429 | { 430 | return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 431 | } 432 | else 433 | { 434 | return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); 435 | } 436 | } 437 | catch (e) 438 | { 439 | return new InvalidFilter(origText, e); 440 | } 441 | }; 442 | RegExpFilter.typeMap = { 443 | OTHER: 1, 444 | SCRIPT: 2, 445 | IMAGE: 4, 446 | STYLESHEET: 8, 447 | OBJECT: 16, 448 | SUBDOCUMENT: 32, 449 | DOCUMENT: 64, 450 | XBL: 1, 451 | PING: 1, 452 | XMLHTTPREQUEST: 2048, 453 | OBJECT_SUBREQUEST: 4096, 454 | DTD: 1, 455 | MEDIA: 16384, 456 | FONT: 32768, 457 | BACKGROUND: 4, 458 | POPUP: 268435456, 459 | ELEMHIDE: 1073741824 460 | }; 461 | RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); 462 | 463 | function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) 464 | { 465 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 466 | this.collapse = collapse; 467 | } 468 | extend(BlockingFilter, RegExpFilter, { 469 | collapse: null 470 | }); 471 | 472 | function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 473 | { 474 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 475 | } 476 | extend(WhitelistFilter, RegExpFilter, { 477 | }); 478 | 479 | function Matcher() 480 | { 481 | this.clear(); 482 | } 483 | Matcher.prototype = { 484 | filterByKeyword: null, 485 | keywordByFilter: null, 486 | clear: function() 487 | { 488 | this.filterByKeyword = createDict(); 489 | this.keywordByFilter = createDict(); 490 | }, 491 | add: function(filter) 492 | { 493 | if (filter.text in this.keywordByFilter) 494 | { 495 | return; 496 | } 497 | var keyword = this.findKeyword(filter); 498 | var oldEntry = this.filterByKeyword[keyword]; 499 | if (typeof oldEntry == "undefined") 500 | { 501 | this.filterByKeyword[keyword] = filter; 502 | } 503 | else if (oldEntry.length == 1) 504 | { 505 | this.filterByKeyword[keyword] = [oldEntry, filter]; 506 | } 507 | else 508 | { 509 | oldEntry.push(filter); 510 | } 511 | this.keywordByFilter[filter.text] = keyword; 512 | }, 513 | remove: function(filter) 514 | { 515 | if (!(filter.text in this.keywordByFilter)) 516 | { 517 | return; 518 | } 519 | var keyword = this.keywordByFilter[filter.text]; 520 | var list = this.filterByKeyword[keyword]; 521 | if (list.length <= 1) 522 | { 523 | delete this.filterByKeyword[keyword]; 524 | } 525 | else 526 | { 527 | var index = list.indexOf(filter); 528 | if (index >= 0) 529 | { 530 | list.splice(index, 1); 531 | if (list.length == 1) 532 | { 533 | this.filterByKeyword[keyword] = list[0]; 534 | } 535 | } 536 | } 537 | delete this.keywordByFilter[filter.text]; 538 | }, 539 | findKeyword: function(filter) 540 | { 541 | var result = ""; 542 | var text = filter.text; 543 | if (Filter.regexpRegExp.test(text)) 544 | { 545 | return result; 546 | } 547 | var match = Filter.optionsRegExp.exec(text); 548 | if (match) 549 | { 550 | text = match.input.substr(0, match.index); 551 | } 552 | if (text.substr(0, 2) == "@@") 553 | { 554 | text = text.substr(2); 555 | } 556 | var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); 557 | if (!candidates) 558 | { 559 | return result; 560 | } 561 | var hash = this.filterByKeyword; 562 | var resultCount = 16777215; 563 | var resultLength = 0; 564 | for (var i = 0, l = candidates.length; i < l; i++) 565 | { 566 | var candidate = candidates[i].substr(1); 567 | var count = candidate in hash ? hash[candidate].length : 0; 568 | if (count < resultCount || count == resultCount && candidate.length > resultLength) 569 | { 570 | result = candidate; 571 | resultCount = count; 572 | resultLength = candidate.length; 573 | } 574 | } 575 | return result; 576 | }, 577 | hasFilter: function(filter) 578 | { 579 | return filter.text in this.keywordByFilter; 580 | }, 581 | getKeywordForFilter: function(filter) 582 | { 583 | if (filter.text in this.keywordByFilter) 584 | { 585 | return this.keywordByFilter[filter.text]; 586 | } 587 | else 588 | { 589 | return null; 590 | } 591 | }, 592 | _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) 593 | { 594 | var list = this.filterByKeyword[keyword]; 595 | for (var i = 0; i < list.length; i++) 596 | { 597 | var filter = list[i]; 598 | if (filter == "#this") 599 | { 600 | filter = list; 601 | } 602 | if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) 603 | { 604 | return filter; 605 | } 606 | } 607 | return null; 608 | }, 609 | matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) 610 | { 611 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 612 | if (candidates === null) 613 | { 614 | candidates = []; 615 | } 616 | candidates.push(""); 617 | for (var i = 0, l = candidates.length; i < l; i++) 618 | { 619 | var substr = candidates[i]; 620 | if (substr in this.filterByKeyword) 621 | { 622 | var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 623 | if (result) 624 | { 625 | return result; 626 | } 627 | } 628 | } 629 | return null; 630 | } 631 | }; 632 | 633 | function CombinedMatcher() 634 | { 635 | this.blacklist = new Matcher(); 636 | this.whitelist = new Matcher(); 637 | this.resultCache = createDict(); 638 | } 639 | CombinedMatcher.maxCacheEntries = 1000; 640 | CombinedMatcher.prototype = { 641 | blacklist: null, 642 | whitelist: null, 643 | resultCache: null, 644 | cacheEntries: 0, 645 | clear: function() 646 | { 647 | this.blacklist.clear(); 648 | this.whitelist.clear(); 649 | this.resultCache = createDict(); 650 | this.cacheEntries = 0; 651 | }, 652 | add: function(filter) 653 | { 654 | if (filter instanceof WhitelistFilter) 655 | { 656 | this.whitelist.add(filter); 657 | } 658 | else 659 | { 660 | this.blacklist.add(filter); 661 | } 662 | if (this.cacheEntries > 0) 663 | { 664 | this.resultCache = createDict(); 665 | this.cacheEntries = 0; 666 | } 667 | }, 668 | remove: function(filter) 669 | { 670 | if (filter instanceof WhitelistFilter) 671 | { 672 | this.whitelist.remove(filter); 673 | } 674 | else 675 | { 676 | this.blacklist.remove(filter); 677 | } 678 | if (this.cacheEntries > 0) 679 | { 680 | this.resultCache = createDict(); 681 | this.cacheEntries = 0; 682 | } 683 | }, 684 | findKeyword: function(filter) 685 | { 686 | if (filter instanceof WhitelistFilter) 687 | { 688 | return this.whitelist.findKeyword(filter); 689 | } 690 | else 691 | { 692 | return this.blacklist.findKeyword(filter); 693 | } 694 | }, 695 | hasFilter: function(filter) 696 | { 697 | if (filter instanceof WhitelistFilter) 698 | { 699 | return this.whitelist.hasFilter(filter); 700 | } 701 | else 702 | { 703 | return this.blacklist.hasFilter(filter); 704 | } 705 | }, 706 | getKeywordForFilter: function(filter) 707 | { 708 | if (filter instanceof WhitelistFilter) 709 | { 710 | return this.whitelist.getKeywordForFilter(filter); 711 | } 712 | else 713 | { 714 | return this.blacklist.getKeywordForFilter(filter); 715 | } 716 | }, 717 | isSlowFilter: function(filter) 718 | { 719 | var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; 720 | if (matcher.hasFilter(filter)) 721 | { 722 | return !matcher.getKeywordForFilter(filter); 723 | } 724 | else 725 | { 726 | return !matcher.findKeyword(filter); 727 | } 728 | }, 729 | matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) 730 | { 731 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 732 | if (candidates === null) 733 | { 734 | candidates = []; 735 | } 736 | candidates.push(""); 737 | var blacklistHit = null; 738 | for (var i = 0, l = candidates.length; i < l; i++) 739 | { 740 | var substr = candidates[i]; 741 | if (substr in this.whitelist.filterByKeyword) 742 | { 743 | var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 744 | if (result) 745 | { 746 | return result; 747 | } 748 | } 749 | if (substr in this.blacklist.filterByKeyword && blacklistHit === null) 750 | { 751 | blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 752 | } 753 | } 754 | return blacklistHit; 755 | }, 756 | matchesAny: function(location, docDomain) 757 | { 758 | var key = location + " " + docDomain + " "; 759 | if (key in this.resultCache) 760 | { 761 | return this.resultCache[key]; 762 | } 763 | var result = this.matchesAnyInternal(location, 0, docDomain, null, null); 764 | if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) 765 | { 766 | this.resultCache = createDict(); 767 | this.cacheEntries = 0; 768 | } 769 | this.resultCache[key] = result; 770 | this.cacheEntries++; 771 | return result; 772 | } 773 | }; 774 | var defaultMatcher = new CombinedMatcher(); 775 | 776 | for (var i = 0; i < rules.length; i++) { 777 | defaultMatcher.add(Filter.fromText(rules[i])); 778 | } 779 | 780 | var subnetIpRangeList = [ 781 | 0,1, 782 | 167772160,184549376, //10.0.0.0/8 783 | 2886729728,2887778304, //172.16.0.0/12 784 | 3232235520,3232301056, //192.168.0.0/16 785 | 2130706432,2130706688 //127.0.0.0/24 786 | ]; 787 | 788 | function convertAddress(ipchars) { 789 | var bytes = ipchars.split('.'); 790 | var result = (bytes[0] << 24) | 791 | (bytes[1] << 16) | 792 | (bytes[2] << 8) | 793 | (bytes[3]); 794 | return result >>> 0; 795 | } 796 | 797 | function check_ipv4(host) { 798 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 799 | if (re_ipv4.test(host)) { 800 | return true; 801 | } 802 | } 803 | function check_ipv6(host) { 804 | var re_ipv6 = /^\[?([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}\]?$/g; 805 | if (re_ipv6.test(host)) { 806 | return true; 807 | } 808 | } 809 | function check_ipv6_dns(dnsstr) { 810 | var re_ipv6 = /([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}(%[0-9]+)?/g; 811 | if (re_ipv6.test(dnsstr)) { 812 | return true; 813 | } 814 | } 815 | function isInSubnetRange(ipRange, intIp) { 816 | for ( var i = 0; i < 10; i += 2 ) { 817 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 818 | return true; 819 | } 820 | } 821 | function getProxyFromIP(strIp) { 822 | var intIp = convertAddress(strIp); 823 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 824 | return direct; 825 | } 826 | return ip_proxy(); 827 | } 828 | 829 | function FindProxyForURL(url, host) { 830 | if ( isPlainHostName(host) === true ) { 831 | return direct; 832 | } 833 | if ( check_ipv4(host) === true ) { 834 | return getProxyFromIP(host); 835 | } 836 | if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) { 837 | return wall_proxy(); 838 | } 839 | return direct; 840 | } 841 | 842 | -------------------------------------------------------------------------------- /ssr/ss_gfw.pac: -------------------------------------------------------------------------------- 1 | var direct = "__DIRECT__"; 2 | if (direct == "__DIR" + "ECT__") direct = "DIRECT;"; 3 | 4 | var wall_proxy = function(){ return "__PROXY__"; }; 5 | var wall_v6_proxy = function(){ return "__PROXY__"; }; 6 | 7 | var nowall_proxy = function(){ return direct; }; 8 | var ip_proxy = function(){ return nowall_proxy(); }; 9 | var ipv6_proxy = function(){ return nowall_proxy(); }; 10 | 11 | /* 12 | * Copyright (C) 2014 breakwa11 13 | * https://github.com/breakwa11/gfw_whitelist 14 | */ 15 | 16 | var rules = __RULES__; 17 | 18 | /* 19 | * This file is part of Adblock Plus , 20 | * Copyright (C) 2006-2014 Eyeo GmbH 21 | * 22 | * Adblock Plus is free software: you can redistribute it and/or modify 23 | * it under the terms of the GNU General Public License version 3 as 24 | * published by the Free Software Foundation. 25 | * 26 | * Adblock Plus is distributed in the hope that it will be useful, 27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | * GNU General Public License for more details. 30 | * 31 | * You should have received a copy of the GNU General Public License 32 | * along with Adblock Plus. If not, see . 33 | */ 34 | 35 | function createDict() 36 | { 37 | var result = {}; 38 | result.__proto__ = null; 39 | return result; 40 | } 41 | 42 | function getOwnPropertyDescriptor(obj, key) 43 | { 44 | if (obj.hasOwnProperty(key)) 45 | { 46 | return obj[key]; 47 | } 48 | return null; 49 | } 50 | 51 | function extend(subclass, superclass, definition) 52 | { 53 | if (Object.__proto__) 54 | { 55 | definition.__proto__ = superclass.prototype; 56 | subclass.prototype = definition; 57 | } 58 | else 59 | { 60 | var tmpclass = function(){}, ret; 61 | tmpclass.prototype = superclass.prototype; 62 | subclass.prototype = new tmpclass(); 63 | subclass.prototype.constructor = superclass; 64 | for (var i in definition) 65 | { 66 | if (definition.hasOwnProperty(i)) 67 | { 68 | subclass.prototype[i] = definition[i]; 69 | } 70 | } 71 | } 72 | } 73 | 74 | function Filter(text) 75 | { 76 | this.text = text; 77 | this.subscriptions = []; 78 | } 79 | Filter.prototype = { 80 | text: null, 81 | subscriptions: null, 82 | toString: function() 83 | { 84 | return this.text; 85 | } 86 | }; 87 | Filter.knownFilters = createDict(); 88 | Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; 89 | Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; 90 | Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; 91 | Filter.fromText = function(text) 92 | { 93 | if (text in Filter.knownFilters) 94 | { 95 | return Filter.knownFilters[text]; 96 | } 97 | var ret; 98 | if (text.charAt(0) == "!") 99 | { 100 | ret = new CommentFilter(text); 101 | } 102 | else 103 | { 104 | ret = RegExpFilter.fromText(text); 105 | } 106 | Filter.knownFilters[ret.text] = ret; 107 | return ret; 108 | }; 109 | 110 | function InvalidFilter(text, reason) 111 | { 112 | Filter.call(this, text); 113 | this.reason = reason; 114 | } 115 | extend(InvalidFilter, Filter, { 116 | reason: null 117 | }); 118 | 119 | function CommentFilter(text) 120 | { 121 | Filter.call(this, text); 122 | } 123 | extend(CommentFilter, Filter, { 124 | }); 125 | 126 | function ActiveFilter(text, domains) 127 | { 128 | Filter.call(this, text); 129 | this.domainSource = domains; 130 | } 131 | extend(ActiveFilter, Filter, { 132 | domainSource: null, 133 | domainSeparator: null, 134 | ignoreTrailingDot: true, 135 | domainSourceIsUpperCase: false, 136 | getDomains: function() 137 | { 138 | var prop = getOwnPropertyDescriptor(this, "domains"); 139 | if (prop) 140 | { 141 | return prop; 142 | } 143 | var domains = null; 144 | if (this.domainSource) 145 | { 146 | var source = this.domainSource; 147 | if (!this.domainSourceIsUpperCase) 148 | { 149 | source = source.toUpperCase(); 150 | } 151 | var list = source.split(this.domainSeparator); 152 | if (list.length == 1 && (list[0]).charAt(0) != "~") 153 | { 154 | domains = createDict(); 155 | domains[""] = false; 156 | if (this.ignoreTrailingDot) 157 | { 158 | list[0] = list[0].replace(/\.+$/, ""); 159 | } 160 | domains[list[0]] = true; 161 | } 162 | else 163 | { 164 | var hasIncludes = false; 165 | for (var i = 0; i < list.length; i++) 166 | { 167 | var domain = list[i]; 168 | if (this.ignoreTrailingDot) 169 | { 170 | domain = domain.replace(/\.+$/, ""); 171 | } 172 | if (domain == "") 173 | { 174 | continue; 175 | } 176 | var include; 177 | if (domain.charAt(0) == "~") 178 | { 179 | include = false; 180 | domain = domain.substr(1); 181 | } 182 | else 183 | { 184 | include = true; 185 | hasIncludes = true; 186 | } 187 | if (!domains) 188 | { 189 | domains = createDict(); 190 | } 191 | domains[domain] = include; 192 | } 193 | domains[""] = !hasIncludes; 194 | } 195 | this.domainSource = null; 196 | } 197 | return this.domains; 198 | }, 199 | sitekeys: null, 200 | isActiveOnDomain: function(docDomain, sitekey) 201 | { 202 | if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) 203 | { 204 | return false; 205 | } 206 | if (!this.getDomains()) 207 | { 208 | return true; 209 | } 210 | if (!docDomain) 211 | { 212 | return this.getDomains()[""]; 213 | } 214 | if (this.ignoreTrailingDot) 215 | { 216 | docDomain = docDomain.replace(/\.+$/, ""); 217 | } 218 | docDomain = docDomain.toUpperCase(); 219 | while (true) 220 | { 221 | if (docDomain in this.getDomains()) 222 | { 223 | return this.domains[docDomain]; 224 | } 225 | var nextDot = docDomain.indexOf("."); 226 | if (nextDot < 0) 227 | { 228 | break; 229 | } 230 | docDomain = docDomain.substr(nextDot + 1); 231 | } 232 | return this.domains[""]; 233 | }, 234 | isActiveOnlyOnDomain: function(docDomain) 235 | { 236 | if (!docDomain || !this.getDomains() || this.getDomains()[""]) 237 | { 238 | return false; 239 | } 240 | if (this.ignoreTrailingDot) 241 | { 242 | docDomain = docDomain.replace(/\.+$/, ""); 243 | } 244 | docDomain = docDomain.toUpperCase(); 245 | for (var domain in this.getDomains()) 246 | { 247 | if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) 248 | { 249 | return false; 250 | } 251 | } 252 | return true; 253 | } 254 | }); 255 | 256 | function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 257 | { 258 | ActiveFilter.call(this, text, domains, sitekeys); 259 | if (contentType != null) 260 | { 261 | this.contentType = contentType; 262 | } 263 | if (matchCase) 264 | { 265 | this.matchCase = matchCase; 266 | } 267 | if (thirdParty != null) 268 | { 269 | this.thirdParty = thirdParty; 270 | } 271 | if (sitekeys != null) 272 | { 273 | this.sitekeySource = sitekeys; 274 | } 275 | if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") 276 | { 277 | var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); 278 | this.regexp = regexp; 279 | } 280 | else 281 | { 282 | this.regexpSource = regexpSource; 283 | } 284 | } 285 | extend(RegExpFilter, ActiveFilter, { 286 | domainSourceIsUpperCase: true, 287 | length: 1, 288 | domainSeparator: "|", 289 | regexpSource: null, 290 | getRegexp: function() 291 | { 292 | var prop = getOwnPropertyDescriptor(this, "regexp"); 293 | if (prop) 294 | { 295 | return prop; 296 | } 297 | 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(/(\.\*)$/, ""); 298 | var regexp = new RegExp(source, this.matchCase ? "" : "i"); 299 | this.regexp = regexp; 300 | return regexp; 301 | }, 302 | contentType: 2147483647, 303 | matchCase: false, 304 | thirdParty: null, 305 | sitekeySource: null, 306 | getSitekeys: function() 307 | { 308 | var prop = getOwnPropertyDescriptor(this, "sitekeys"); 309 | if (prop) 310 | { 311 | return prop; 312 | } 313 | var sitekeys = null; 314 | if (this.sitekeySource) 315 | { 316 | sitekeys = this.sitekeySource.split("|"); 317 | this.sitekeySource = null; 318 | } 319 | this.sitekeys = sitekeys; 320 | return this.sitekeys; 321 | }, 322 | matches: function(location, contentType, docDomain, thirdParty, sitekey) 323 | { 324 | if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) 325 | { 326 | return true; 327 | } 328 | return false; 329 | } 330 | }); 331 | RegExpFilter.prototype["0"] = "#this"; 332 | RegExpFilter.fromText = function(text) 333 | { 334 | var blocking = true; 335 | var origText = text; 336 | if (text.indexOf("@@") == 0) 337 | { 338 | blocking = false; 339 | text = text.substr(2); 340 | } 341 | var contentType = null; 342 | var matchCase = null; 343 | var domains = null; 344 | var sitekeys = null; 345 | var thirdParty = null; 346 | var collapse = null; 347 | var options; 348 | var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; 349 | if (match) 350 | { 351 | options = match[1].toUpperCase().split(","); 352 | text = match.input.substr(0, match.index); 353 | for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) 354 | { 355 | var option = options[_loopIndex6]; 356 | var value = null; 357 | var separatorIndex = option.indexOf("="); 358 | if (separatorIndex >= 0) 359 | { 360 | value = option.substr(separatorIndex + 1); 361 | option = option.substr(0, separatorIndex); 362 | } 363 | option = option.replace(/-/, "_"); 364 | if (option in RegExpFilter.typeMap) 365 | { 366 | if (contentType == null) 367 | { 368 | contentType = 0; 369 | } 370 | contentType |= RegExpFilter.typeMap[option]; 371 | } 372 | else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) 373 | { 374 | if (contentType == null) 375 | { 376 | contentType = RegExpFilter.prototype.contentType; 377 | } 378 | contentType &= ~RegExpFilter.typeMap[option.substr(1)]; 379 | } 380 | else if (option == "MATCH_CASE") 381 | { 382 | matchCase = true; 383 | } 384 | else if (option == "~MATCH_CASE") 385 | { 386 | matchCase = false; 387 | } 388 | else if (option == "DOMAIN" && typeof value != "undefined") 389 | { 390 | domains = value; 391 | } 392 | else if (option == "THIRD_PARTY") 393 | { 394 | thirdParty = true; 395 | } 396 | else if (option == "~THIRD_PARTY") 397 | { 398 | thirdParty = false; 399 | } 400 | else if (option == "COLLAPSE") 401 | { 402 | collapse = true; 403 | } 404 | else if (option == "~COLLAPSE") 405 | { 406 | collapse = false; 407 | } 408 | else if (option == "SITEKEY" && typeof value != "undefined") 409 | { 410 | sitekeys = value; 411 | } 412 | else 413 | { 414 | return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); 415 | } 416 | } 417 | } 418 | if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) 419 | { 420 | if (contentType == null) 421 | { 422 | contentType = RegExpFilter.prototype.contentType; 423 | } 424 | contentType &= ~RegExpFilter.typeMap.DOCUMENT; 425 | } 426 | try 427 | { 428 | if (blocking) 429 | { 430 | return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); 431 | } 432 | else 433 | { 434 | return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); 435 | } 436 | } 437 | catch (e) 438 | { 439 | return new InvalidFilter(origText, e); 440 | } 441 | }; 442 | RegExpFilter.typeMap = { 443 | OTHER: 1, 444 | SCRIPT: 2, 445 | IMAGE: 4, 446 | STYLESHEET: 8, 447 | OBJECT: 16, 448 | SUBDOCUMENT: 32, 449 | DOCUMENT: 64, 450 | XBL: 1, 451 | PING: 1, 452 | XMLHTTPREQUEST: 2048, 453 | OBJECT_SUBREQUEST: 4096, 454 | DTD: 1, 455 | MEDIA: 16384, 456 | FONT: 32768, 457 | BACKGROUND: 4, 458 | POPUP: 268435456, 459 | ELEMHIDE: 1073741824 460 | }; 461 | RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); 462 | 463 | function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) 464 | { 465 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 466 | this.collapse = collapse; 467 | } 468 | extend(BlockingFilter, RegExpFilter, { 469 | collapse: null 470 | }); 471 | 472 | function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) 473 | { 474 | RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); 475 | } 476 | extend(WhitelistFilter, RegExpFilter, { 477 | }); 478 | 479 | function Matcher() 480 | { 481 | this.clear(); 482 | } 483 | Matcher.prototype = { 484 | filterByKeyword: null, 485 | keywordByFilter: null, 486 | clear: function() 487 | { 488 | this.filterByKeyword = createDict(); 489 | this.keywordByFilter = createDict(); 490 | }, 491 | add: function(filter) 492 | { 493 | if (filter.text in this.keywordByFilter) 494 | { 495 | return; 496 | } 497 | var keyword = this.findKeyword(filter); 498 | var oldEntry = this.filterByKeyword[keyword]; 499 | if (typeof oldEntry == "undefined") 500 | { 501 | this.filterByKeyword[keyword] = filter; 502 | } 503 | else if (oldEntry.length == 1) 504 | { 505 | this.filterByKeyword[keyword] = [oldEntry, filter]; 506 | } 507 | else 508 | { 509 | oldEntry.push(filter); 510 | } 511 | this.keywordByFilter[filter.text] = keyword; 512 | }, 513 | remove: function(filter) 514 | { 515 | if (!(filter.text in this.keywordByFilter)) 516 | { 517 | return; 518 | } 519 | var keyword = this.keywordByFilter[filter.text]; 520 | var list = this.filterByKeyword[keyword]; 521 | if (list.length <= 1) 522 | { 523 | delete this.filterByKeyword[keyword]; 524 | } 525 | else 526 | { 527 | var index = list.indexOf(filter); 528 | if (index >= 0) 529 | { 530 | list.splice(index, 1); 531 | if (list.length == 1) 532 | { 533 | this.filterByKeyword[keyword] = list[0]; 534 | } 535 | } 536 | } 537 | delete this.keywordByFilter[filter.text]; 538 | }, 539 | findKeyword: function(filter) 540 | { 541 | var result = ""; 542 | var text = filter.text; 543 | if (Filter.regexpRegExp.test(text)) 544 | { 545 | return result; 546 | } 547 | var match = Filter.optionsRegExp.exec(text); 548 | if (match) 549 | { 550 | text = match.input.substr(0, match.index); 551 | } 552 | if (text.substr(0, 2) == "@@") 553 | { 554 | text = text.substr(2); 555 | } 556 | var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); 557 | if (!candidates) 558 | { 559 | return result; 560 | } 561 | var hash = this.filterByKeyword; 562 | var resultCount = 16777215; 563 | var resultLength = 0; 564 | for (var i = 0, l = candidates.length; i < l; i++) 565 | { 566 | var candidate = candidates[i].substr(1); 567 | var count = candidate in hash ? hash[candidate].length : 0; 568 | if (count < resultCount || count == resultCount && candidate.length > resultLength) 569 | { 570 | result = candidate; 571 | resultCount = count; 572 | resultLength = candidate.length; 573 | } 574 | } 575 | return result; 576 | }, 577 | hasFilter: function(filter) 578 | { 579 | return filter.text in this.keywordByFilter; 580 | }, 581 | getKeywordForFilter: function(filter) 582 | { 583 | if (filter.text in this.keywordByFilter) 584 | { 585 | return this.keywordByFilter[filter.text]; 586 | } 587 | else 588 | { 589 | return null; 590 | } 591 | }, 592 | _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) 593 | { 594 | var list = this.filterByKeyword[keyword]; 595 | for (var i = 0; i < list.length; i++) 596 | { 597 | var filter = list[i]; 598 | if (filter == "#this") 599 | { 600 | filter = list; 601 | } 602 | if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) 603 | { 604 | return filter; 605 | } 606 | } 607 | return null; 608 | }, 609 | matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) 610 | { 611 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 612 | if (candidates === null) 613 | { 614 | candidates = []; 615 | } 616 | candidates.push(""); 617 | for (var i = 0, l = candidates.length; i < l; i++) 618 | { 619 | var substr = candidates[i]; 620 | if (substr in this.filterByKeyword) 621 | { 622 | var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 623 | if (result) 624 | { 625 | return result; 626 | } 627 | } 628 | } 629 | return null; 630 | } 631 | }; 632 | 633 | function CombinedMatcher() 634 | { 635 | this.blacklist = new Matcher(); 636 | this.whitelist = new Matcher(); 637 | this.resultCache = createDict(); 638 | } 639 | CombinedMatcher.maxCacheEntries = 1000; 640 | CombinedMatcher.prototype = { 641 | blacklist: null, 642 | whitelist: null, 643 | resultCache: null, 644 | cacheEntries: 0, 645 | clear: function() 646 | { 647 | this.blacklist.clear(); 648 | this.whitelist.clear(); 649 | this.resultCache = createDict(); 650 | this.cacheEntries = 0; 651 | }, 652 | add: function(filter) 653 | { 654 | if (filter instanceof WhitelistFilter) 655 | { 656 | this.whitelist.add(filter); 657 | } 658 | else 659 | { 660 | this.blacklist.add(filter); 661 | } 662 | if (this.cacheEntries > 0) 663 | { 664 | this.resultCache = createDict(); 665 | this.cacheEntries = 0; 666 | } 667 | }, 668 | remove: function(filter) 669 | { 670 | if (filter instanceof WhitelistFilter) 671 | { 672 | this.whitelist.remove(filter); 673 | } 674 | else 675 | { 676 | this.blacklist.remove(filter); 677 | } 678 | if (this.cacheEntries > 0) 679 | { 680 | this.resultCache = createDict(); 681 | this.cacheEntries = 0; 682 | } 683 | }, 684 | findKeyword: function(filter) 685 | { 686 | if (filter instanceof WhitelistFilter) 687 | { 688 | return this.whitelist.findKeyword(filter); 689 | } 690 | else 691 | { 692 | return this.blacklist.findKeyword(filter); 693 | } 694 | }, 695 | hasFilter: function(filter) 696 | { 697 | if (filter instanceof WhitelistFilter) 698 | { 699 | return this.whitelist.hasFilter(filter); 700 | } 701 | else 702 | { 703 | return this.blacklist.hasFilter(filter); 704 | } 705 | }, 706 | getKeywordForFilter: function(filter) 707 | { 708 | if (filter instanceof WhitelistFilter) 709 | { 710 | return this.whitelist.getKeywordForFilter(filter); 711 | } 712 | else 713 | { 714 | return this.blacklist.getKeywordForFilter(filter); 715 | } 716 | }, 717 | isSlowFilter: function(filter) 718 | { 719 | var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; 720 | if (matcher.hasFilter(filter)) 721 | { 722 | return !matcher.getKeywordForFilter(filter); 723 | } 724 | else 725 | { 726 | return !matcher.findKeyword(filter); 727 | } 728 | }, 729 | matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) 730 | { 731 | var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 732 | if (candidates === null) 733 | { 734 | candidates = []; 735 | } 736 | candidates.push(""); 737 | var blacklistHit = null; 738 | for (var i = 0, l = candidates.length; i < l; i++) 739 | { 740 | var substr = candidates[i]; 741 | if (substr in this.whitelist.filterByKeyword) 742 | { 743 | var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 744 | if (result) 745 | { 746 | return result; 747 | } 748 | } 749 | if (substr in this.blacklist.filterByKeyword && blacklistHit === null) 750 | { 751 | blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); 752 | } 753 | } 754 | return blacklistHit; 755 | }, 756 | matchesAny: function(location, docDomain) 757 | { 758 | var key = location + " " + docDomain + " "; 759 | if (key in this.resultCache) 760 | { 761 | return this.resultCache[key]; 762 | } 763 | var result = this.matchesAnyInternal(location, 0, docDomain, null, null); 764 | if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) 765 | { 766 | this.resultCache = createDict(); 767 | this.cacheEntries = 0; 768 | } 769 | this.resultCache[key] = result; 770 | this.cacheEntries++; 771 | return result; 772 | } 773 | }; 774 | var defaultMatcher = new CombinedMatcher(); 775 | 776 | for (var i = 0; i < rules.length; i++) { 777 | defaultMatcher.add(Filter.fromText(rules[i])); 778 | } 779 | 780 | var subnetIpRangeList = [ 781 | 0,1, 782 | 167772160,184549376, //10.0.0.0/8 783 | 2886729728,2887778304, //172.16.0.0/12 784 | 3232235520,3232301056, //192.168.0.0/16 785 | 2130706432,2130706688 //127.0.0.0/24 786 | ]; 787 | 788 | function convertAddress(ipchars) { 789 | var bytes = ipchars.split('.'); 790 | var result = (bytes[0] << 24) | 791 | (bytes[1] << 16) | 792 | (bytes[2] << 8) | 793 | (bytes[3]); 794 | return result >>> 0; 795 | } 796 | 797 | function check_ipv4(host) { 798 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 799 | if (re_ipv4.test(host)) { 800 | return true; 801 | } 802 | } 803 | function check_ipv6(host) { 804 | var re_ipv6 = /^\[?([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}\]?$/g; 805 | if (re_ipv6.test(host)) { 806 | return true; 807 | } 808 | } 809 | function check_ipv6_dns(dnsstr) { 810 | var re_ipv6 = /([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}(%[0-9]+)?/g; 811 | if (re_ipv6.test(dnsstr)) { 812 | return true; 813 | } 814 | } 815 | function isInSubnetRange(ipRange, intIp) { 816 | for ( var i = 0; i < 10; i += 2 ) { 817 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 818 | return true; 819 | } 820 | } 821 | function getProxyFromIP(strIp) { 822 | var intIp = convertAddress(strIp); 823 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 824 | return direct; 825 | } 826 | return ip_proxy(); 827 | } 828 | 829 | function FindProxyForURL(url, host) { 830 | if ( isPlainHostName(host) === true ) { 831 | return direct; 832 | } 833 | if ( check_ipv4(host) === true ) { 834 | return getProxyFromIP(host); 835 | } 836 | if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) { 837 | return wall_proxy(); 838 | } 839 | return direct; 840 | } 841 | 842 | -------------------------------------------------------------------------------- /ssr/ss_lanip.pac: -------------------------------------------------------------------------------- 1 | var direct = "__DIRECT__"; 2 | if (direct == "__DIR" + "ECT__") direct = "DIRECT;"; 3 | 4 | var wall_proxy = function(){ return "__PROXY__"; }; 5 | var wall_v6_proxy = function(){ return "__PROXY__"; }; 6 | 7 | var nowall_proxy = function(){ return direct; }; 8 | var ip_proxy = function(){ return wall_proxy(); }; 9 | var ipv6_proxy = function(){ return wall_v6_proxy(); }; 10 | 11 | /* 12 | * Copyright (C) 2014 breakwa11 13 | * https://github.com/breakwa11/gfw_whitelist 14 | */ 15 | 16 | var subnetIpRangeList = [ 17 | 0,1, 18 | 167772160,184549376, //10.0.0.0/8 19 | 2886729728,2887778304, //172.16.0.0/12 20 | 3232235520,3232301056, //192.168.0.0/16 21 | 2130706432,2130706688 //127.0.0.0/24 22 | ]; 23 | 24 | var hasOwnProperty = Object.hasOwnProperty; 25 | 26 | function check_ipv4(host) { 27 | var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; 28 | if (re_ipv4.test(host)) { 29 | return true; 30 | } 31 | } 32 | function check_ipv6(host) { 33 | var re_ipv6 = /^\[?([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}\]?$/g; 34 | if (re_ipv6.test(host)) { 35 | return true; 36 | } 37 | } 38 | function check_ipv6_dns(dnsstr) { 39 | var re_ipv6 = /([a-fA-F0-9]{0,4}\:){1,7}[a-fA-F0-9]{0,4}(%[0-9]+)?/g; 40 | if (re_ipv6.test(dnsstr)) { 41 | return true; 42 | } 43 | } 44 | function convertAddress(ipchars) { 45 | var bytes = ipchars.split('.'); 46 | var result = (bytes[0] << 24) | 47 | (bytes[1] << 16) | 48 | (bytes[2] << 8) | 49 | (bytes[3]); 50 | return result >>> 0; 51 | } 52 | function isInSubnetRange(ipRange, intIp) { 53 | for ( var i = 0; i < 10; i += 2 ) { 54 | if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) 55 | return true; 56 | } 57 | } 58 | function getProxyFromIP(strIp) { 59 | var intIp = convertAddress(strIp); 60 | if ( isInSubnetRange(subnetIpRangeList, intIp) ) { 61 | return direct; 62 | } 63 | return ip_proxy(); 64 | } 65 | function FindProxyForURL(url, host) { 66 | if ( isPlainHostName(host) === true ) { 67 | return direct; 68 | } 69 | if ( check_ipv4(host) === true ) { 70 | return getProxyFromIP(host); 71 | } 72 | if ( check_ipv6(host) === true ) { 73 | return ipv6_proxy(); 74 | } 75 | return wall_proxy(); 76 | } 77 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pac测试 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test_cases = [ 2 | 1, "a.b.c.d.com", 3 | 0, "127.0.0.1", 4 | 0, "qq.com", 5 | 0, "im.qq.com", 6 | 0, "www.imqq.com", 7 | 1, "qqq.com", 8 | 1, "google.com", 9 | 0, "localhost" 10 | ]; 11 | 12 | function isPlainHostName(host) { 13 | if ( host.toLowerCase() == 'localhost' ) 14 | return true; 15 | return false; 16 | } 17 | 18 | function dnsResolve(host) { 19 | return "27.40.0.0"; 20 | return "27.50.96.0"; 21 | return "27.50.128.0"; 22 | } 23 | 24 | function dnsResolveEx(host) { 25 | return "27.40.0.0"; 26 | return "27.50.96.0"; 27 | return "27.50.128.0"; 28 | } 29 | 30 | function isInNet(ip, ipstart, ipmask) { 31 | return false; 32 | } 33 | 34 | function shExpMatch(a, b) { 35 | return false; 36 | } 37 | 38 | function test(url, host) { 39 | ret = FindProxyForURLEx(url, host); 40 | if ( typeof(direct) == "undefined" ) { 41 | if ( ret.toLowerCase().indexOf("direct") >= 0 ) { 42 | return 0; 43 | } 44 | return 1; 45 | } else if ( ret === direct ) 46 | return 0; 47 | else 48 | return 1; 49 | } 50 | 51 | function output_result(out_obj) { 52 | output.value = ""; 53 | for (var j = 0; j < test_cases.length; j+=2) { 54 | var test_case = test_cases[j+1]; 55 | var test_result = test(test_case, test_case); 56 | var out_line = "" + test_result + " " + test_case + " "; 57 | if ( test_result === test_cases[j] ) { 58 | out_line = out_line + "Pass"; 59 | } else { 60 | out_line = out_line + "NOT Pass"; 61 | } 62 | out_obj.value = out_obj.value + out_line + "\n"; 63 | } 64 | var start = new Date(); 65 | if ( test_cases.length > 1 ) { 66 | for (var j = 0; j < 100000; ++j) { 67 | var test_case = test_cases[1]; 68 | test(test_case, test_case); 69 | } 70 | } 71 | var end = new Date(); 72 | alert(String(end - start) + "ms in 100,000 tests"); 73 | } 74 | 75 | function begin_test() { 76 | var output = document.getElementById("output"); 77 | output_result( output ); 78 | } 79 | 80 | function test_one() { 81 | var input = document.getElementById("input"); 82 | var result_obj = document.getElementById("result"); 83 | result = test(input.value, input.value); 84 | if ( result === 1 ) 85 | result_obj.value = "Proxy"; 86 | else 87 | result_obj.value = "Direct"; 88 | } 89 | --------------------------------------------------------------------------------