2015-11-27 23:47:27 +03:00
|
|
|
var fs = require('fs');
|
|
|
|
|
|
|
|
var input = fs.readFileSync('dump.csv').toString();
|
|
|
|
var outputDir = 'generated-PACs';
|
|
|
|
|
|
|
|
try {
|
|
|
|
fs.mkdirSync( outputDir );
|
|
|
|
} catch(e) {
|
|
|
|
if ( e.code != 'EEXIST' ) throw e;
|
|
|
|
}
|
|
|
|
|
|
|
|
var punycode = require('punycode')
|
|
|
|
|
|
|
|
/*
|
|
|
|
CVS Format:
|
|
|
|
|
|
|
|
IP(s);host(s);URL(s);organization(s);reason;yyyy-mm-dd
|
|
|
|
|
|
|
|
* multiple values are joind by " | "
|
|
|
|
* url may have wierd protocol, e.g.: Newcamd525://
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
var columnsSep = ';';
|
|
|
|
var valuesSep = /\s*\|\s*/g;
|
|
|
|
|
|
|
|
var ips = [], hosts = [], urls = [], orgs = [], date, reason;
|
|
|
|
|
|
|
|
for(var line of input.trim().split(/\r?\n/g).slice(1)) {
|
|
|
|
var values = line.split( columnsSep );
|
|
|
|
var newIps = values.shift().split( valuesSep );
|
|
|
|
var newHosts = values.shift().split( valuesSep ).map( punycode.toASCII ).map( host => host.replace(/\.+$/g) );
|
|
|
|
var newUrls = values.shift().split( valuesSep );
|
|
|
|
var newOrgs = values.shift().split( valuesSep );
|
|
|
|
var newDate = values.pop();
|
|
|
|
var newReason = values.join(';');
|
|
|
|
ips.push.apply(ips, newIps);
|
|
|
|
hosts.push.apply(hosts, newHosts);
|
|
|
|
}
|
|
|
|
|
|
|
|
function toHash(arr) {
|
|
|
|
var res = {};
|
|
|
|
arr.forEach( el => res[el] = true );
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
var ipsHash = toHash(ips);
|
|
|
|
var hostsHash = toHash(hosts);
|
|
|
|
|
|
|
|
// Remove duplicates and sort.
|
|
|
|
var ips = Object.keys(ipsHash).sort();
|
|
|
|
var hosts = Object.keys(hostsHash).sort();
|
|
|
|
|
|
|
|
function FindProxyForURL(url, host) {
|
|
|
|
// ProstoVPN.AntiZapret PAC-ip File
|
|
|
|
// Generated on Sun Nov 22 10:12:29 MSK 2015
|
|
|
|
|
|
|
|
// The whole PAC script is reevaluated on each call of this function.
|
|
|
|
|
|
|
|
host = host.replace(/\.+$/).toLowerCase(); // E.g. WinHTTP may be nasty.
|
|
|
|
|
|
|
|
// HTTPS proxy is a HTTP proxy over SSL. It is NOT CONNECT proxy!
|
|
|
|
// Supported only in Chrome and Firefox.
|
|
|
|
// http://www.chromium.org/developers/design-documents/secure-web-proxy
|
|
|
|
// This is to bypass FULL DPI
|
|
|
|
var isIE = /*@cc_on!@*/!1;
|
|
|
|
var viaProxy = isIE
|
|
|
|
? 'PROXY proxy.antizapret.prostovpn.org:3128; DIRECT'
|
|
|
|
: 'HTTPS proxy.antizapret.prostovpn.org:3143; PROXY proxy.antizapret.prostovpn.org:3128; DIRECT';
|
|
|
|
|
|
|
|
return IFPROXY() ? viaProxy : 'DIRECT';
|
|
|
|
}
|
|
|
|
|
|
|
|
var pacTemplate = FindProxyForURL.toString();
|
|
|
|
|
|
|
|
function stringifyCall() {
|
|
|
|
var fun = arguments[0];
|
|
|
|
var args = [].slice.call( arguments, 1 )
|
|
|
|
.map( a => typeof a !== 'string' ? JSON.stringify(a) : a ).join(', ');
|
|
|
|
return '('+fun+')('+args+')';
|
|
|
|
}
|
|
|
|
|
|
|
|
function produceOutput() {
|
|
|
|
var args = [].slice.call( arguments )
|
|
|
|
var scriptName = args.shift();
|
|
|
|
|
|
|
|
var script = pacTemplate
|
|
|
|
.replace( 'IFPROXY()', stringifyCall.apply(this, args) );
|
|
|
|
fs.writeFileSync(outputDir +'/'+ scriptName +'.js', script);
|
|
|
|
}
|
|
|
|
|
|
|
|
// BLOCKED IPS ARRAY
|
|
|
|
|
|
|
|
function ifProxyByIp(host, blockedIpsArray) {
|
|
|
|
|
|
|
|
// Internet Explorer
|
|
|
|
if (!Array.prototype.indexOf)
|
|
|
|
Array.prototype.indexOf = function(obj, start) {
|
|
|
|
for (var i = (start || 0), j = this.length; i < j; i++)
|
|
|
|
if (this[i] === obj) return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return blockedIpsArray.indexOf( dnsResolve(host) ) !== -1
|
|
|
|
}
|
|
|
|
|
|
|
|
produceOutput('blocked-ips-indexOf', ifProxyByIp, 'host', ips);
|
|
|
|
|
|
|
|
// BLOCKED IPS SWITCH
|
|
|
|
|
|
|
|
function ifProxyBySwitch(host) {
|
|
|
|
switch( dnsResolve(host) ) {
|
|
|
|
/*{CASES}*/
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var cases = ips.map( ip => 'case "'+ip+'":' ).join('\n') +'\nreturn true;';
|
|
|
|
var ifProxySwitchStr = ifProxyBySwitch.toString().replace('/*{CASES}*/', cases);
|
|
|
|
|
|
|
|
produceOutput('blocked-ips-switch', ifProxySwitchStr, 'host' );
|
|
|
|
|
|
|
|
// BLOCKED IPS BINARY
|
|
|
|
|
|
|
|
function ifBinaryFound(target, sortedArray) {
|
|
|
|
var istart = 0;
|
|
|
|
var iend = sortedArray.length - 1;
|
|
|
|
|
|
|
|
while (istart < iend) {
|
|
|
|
var imid = istart + Math.floor( (iend - istart)*0.5 );
|
|
|
|
if (target > sortedArray[imid])
|
|
|
|
istart = imid + 1;
|
|
|
|
else
|
|
|
|
iend = imid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return target === sortedArray[iend];
|
|
|
|
}
|
|
|
|
|
|
|
|
produceOutput('blocked-ips-binary', ifBinaryFound, 'dnsResolve(host)', ips);
|
|
|
|
|
|
|
|
// BLOCKED HOSTS BINARY
|
|
|
|
produceOutput('blocked-hosts-binary', ifBinaryFound, 'host', hosts);
|
|
|
|
|
|
|
|
// REVERSED BINARY
|
|
|
|
|
|
|
|
var reverse = str => str.split('').reverse().join('');
|
|
|
|
var reversedHosts = hosts.map( reverse ).sort();
|
|
|
|
|
|
|
|
function ifReversedBinaryFound(host, sortedArray) {
|
|
|
|
target = host.split('').reverse().join('');
|
|
|
|
var istart = 0;
|
|
|
|
var iend = sortedArray.length - 1;
|
|
|
|
|
|
|
|
while (istart < iend) {
|
|
|
|
var imid = istart + Math.floor( (iend - istart)*0.5 );
|
|
|
|
if (target > sortedArray[imid])
|
|
|
|
istart = imid + 1;
|
|
|
|
else
|
|
|
|
iend = imid;
|
|
|
|
}
|
|
|
|
return dnsDomainIs( host, sortedArray[iend].split('').reverse().join('') );
|
|
|
|
}
|
|
|
|
|
|
|
|
produceOutput('blocked-hosts-reversed-binary', ifReversedBinaryFound, 'host', reversedHosts);
|
|
|
|
|
|
|
|
// REVERSED HOSTS SWITCH
|
|
|
|
|
|
|
|
function populateTrie(trie, doms) {
|
|
|
|
|
|
|
|
var dom = doms.pop();
|
|
|
|
if (!doms.length || doms.length === 1 && doms[0] === 'www') {
|
|
|
|
trie[''] = trie[''] || [];
|
|
|
|
trie[''].push( dom )
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trie[''] && trie[''].indexOf(dom) !== -1) // Subdomain of a blocked domain.
|
|
|
|
return trie;
|
|
|
|
|
|
|
|
trie[dom] = trie[dom] || {};
|
|
|
|
|
|
|
|
populateTrie( trie[dom], doms );
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
var trie = {};
|
|
|
|
for(var host of hosts) {
|
|
|
|
var doms = host.split('.');
|
|
|
|
populateTrie(trie, doms);
|
|
|
|
}
|
|
|
|
|
|
|
|
function trieToSwitch(trie, indent) {
|
|
|
|
var _indent = indent || '';
|
|
|
|
var indent = _indent + ' ';
|
|
|
|
var keys = Object.keys(trie).sort();
|
|
|
|
|
|
|
|
if (!trie[''] && keys.length === 1) {
|
|
|
|
var key = keys[0];
|
|
|
|
return _indent + 'if (doms.pop() === "'+key+'")\n'+ trieToSwitch(trie[key], indent);
|
|
|
|
}
|
|
|
|
|
|
|
|
var cases = '';
|
|
|
|
if (trie['']) {
|
|
|
|
var values = trie[''].sort();
|
|
|
|
|
|
|
|
if (values.length === 1 && keys.length === 1)
|
|
|
|
return _indent + 'return doms.pop() === "'+values[0]+'";\n';
|
|
|
|
|
|
|
|
cases =
|
|
|
|
values.filter( v => v ).map( val => indent +'case "'+val+'":\n' ).join('') + indent +' return true;\n';
|
|
|
|
|
|
|
|
delete trie[''];
|
|
|
|
keys = Object.keys(trie).sort();
|
|
|
|
}
|
|
|
|
|
|
|
|
cases += keys.filter( k => k ).map(
|
|
|
|
key => {
|
|
|
|
var tmp = trieToSwitch( trie[key], indent+' ');
|
|
|
|
if (!/^\s*return/.test(tmp))
|
|
|
|
tmp += indent+' break;\n';
|
|
|
|
|
|
|
|
return indent +'case "'+key+'":\n' +tmp;
|
|
|
|
}
|
|
|
|
).join('');
|
|
|
|
|
|
|
|
return ''
|
|
|
|
+ _indent +'switch( doms.pop() ) {\n'
|
|
|
|
+ cases
|
|
|
|
+ _indent +'}\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
function ifProxyByTrie(host) {
|
|
|
|
//SWITCH
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var ifProxyByTrieStr = ifProxyByTrie.toString().replace('//SWITCH', trieToSwitch(trie, ' '));
|
|
|
|
|
|
|
|
produceOutput('blocked-hosts-switch', ifProxyByTrieStr, 'host' );
|
|
|
|
|
|
|
|
/* REVERSED HOSTS BINARY TRIE
|
|
|
|
|
|
|
|
function populateBinTrie(trie, doms) {
|
|
|
|
var dom = doms.pop();
|
|
|
|
if (!doms.length || doms.length === 1 && doms[0] === 'www') {
|
|
|
|
trie[dom] = 'blocked';
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trie[dom] === 'blocked') // Subdomain of a blocked domain.
|
|
|
|
return trie;
|
|
|
|
|
|
|
|
trie[dom] = trie[dom] || {};
|
|
|
|
populateBinTrie( trie[dom], doms );
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
var trie = {};
|
|
|
|
for(var host of hosts) {
|
|
|
|
var doms = host.split('.');
|
|
|
|
populateBinTrie(trie, doms);
|
|
|
|
}
|
|
|
|
|
|
|
|
function trie2sorted(trie) {
|
|
|
|
if (trie === 'blocked')
|
|
|
|
return trie;
|
|
|
|
|
|
|
|
var keys = Object.keys(trie).sort();
|
|
|
|
return keys.map( key => trie[key] !== 'blocked' ? [ key, trie2sorted( trie[key] ) ] : [key] );
|
|
|
|
}
|
|
|
|
|
|
|
|
var sortedTrie = trie2sorted(trie);
|
|
|
|
|
|
|
|
function ifProxyByBinTrie(host, sortedTrie) {
|
|
|
|
|
|
|
|
var doms = host.split('.');
|
|
|
|
|
|
|
|
function ifBinaryBlocked(sortedTrie) {
|
|
|
|
var target = doms.pop();
|
|
|
|
if (!target)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var istart = 0;
|
|
|
|
var iend = sortedTrie.length - 1;
|
|
|
|
|
|
|
|
while (istart < iend) {
|
|
|
|
var imid = istart + Math.floor( (iend - istart)*0.5 );
|
|
|
|
if (target > sortedTrie[imid][0])
|
|
|
|
istart = imid + 1;
|
|
|
|
else
|
|
|
|
iend = imid;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target !== sortedTrie[iend][0])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return sortedTrie[iend].length < 2 ? true : ifBinaryBlocked( sortedTrie[iend][1] );
|
|
|
|
}
|
|
|
|
|
|
|
|
return ifBinaryBlocked(sortedTrie)
|
|
|
|
}
|
|
|
|
|
|
|
|
produceOutput('blocked-hosts-binary-trie', ifProxyByBinTrie, 'host', sortedTrie);
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
// BLOCKED HOSTS PLAIN SWITCH
|
|
|
|
|
|
|
|
function ifProxyByPlainSwitch(host) {
|
|
|
|
|
|
|
|
function ifBlocked(host) {
|
|
|
|
switch( host ) {
|
|
|
|
//CASES
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var doms = host.split('.');
|
|
|
|
|
2015-11-28 03:32:25 +03:00
|
|
|
for( var endi = doms.length-1; endi >= 0; --endi )
|
2015-11-27 23:47:27 +03:00
|
|
|
if (ifBlocked( doms.slice( endi ).join('.') ))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var cases = hosts.map( host => 'case "'+host+'":' ).join('\n') +'\nreturn true;';
|
|
|
|
var tmp = ifProxyByPlainSwitch.toString().replace('//CASES', cases);
|
|
|
|
|
2015-11-28 03:32:25 +03:00
|
|
|
produceOutput( 'blocked-hosts-plain-switch', tmp, 'host' );
|
|
|
|
|
|
|
|
|
|
|
|
// SIMPLY TRIE
|
|
|
|
|
|
|
|
function populateSimpleTrie(trie, letters) {
|
|
|
|
|
|
|
|
if (!letters.length || !trie)
|
|
|
|
return trie;
|
|
|
|
|
|
|
|
var letter = letters.shift();
|
|
|
|
|
|
|
|
if (!letters.length) {
|
|
|
|
trie[letter] = ['', false];
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (trie[letter] === ['', false]) // Subdomain of a blocked domain.
|
|
|
|
return trie;
|
|
|
|
|
|
|
|
if (!trie[letter]) {
|
|
|
|
trie[letter] = [letters.join(''), false]
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
var arr = trie[letter];
|
|
|
|
var link = arr[0].split('');
|
|
|
|
var linkTrie = arr[1];
|
|
|
|
|
|
|
|
function commonPrefix(wa, wb) {
|
|
|
|
var len = Math.min(wa.length, wb.length);
|
|
|
|
var i = -1;
|
|
|
|
while(++i < len)
|
|
|
|
if (wa[i] !== wb[i])
|
|
|
|
break;
|
|
|
|
return wa.slice(0, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
var prefix = commonPrefix(link, letters);
|
|
|
|
|
|
|
|
var suffixLetters = letters.slice(prefix.length);
|
|
|
|
var suffixLink = link.slice(prefix.length);
|
|
|
|
|
|
|
|
var newTrie = {};
|
|
|
|
if (!suffixLink.length)
|
|
|
|
var newTrie = linkTrie;
|
|
|
|
if (!suffixLetters.length)
|
|
|
|
var newTrie = false;
|
|
|
|
|
|
|
|
if (newTrie) {
|
|
|
|
newTrie = populateSimpleTrie( newTrie, suffixLetters );
|
|
|
|
newTrie = populateSimpleTrie( newTrie, suffixLink );
|
|
|
|
}
|
|
|
|
trie[letter] = [prefix.join(''), newTrie];
|
|
|
|
|
|
|
|
return trie;
|
|
|
|
}
|
|
|
|
|
|
|
|
var simpleTrie = {};
|
|
|
|
for(var host of hosts)
|
|
|
|
populateSimpleTrie(simpleTrie, host.split('').reverse());
|
|
|
|
|
|
|
|
function ifProxyBySimpleTrie(host, simpleTrie) {
|
|
|
|
var letters = host.split('').reverse();
|
|
|
|
while(letters.length) {
|
|
|
|
var letter = letters.shift();
|
|
|
|
var arr = simpleTrie[letter];
|
|
|
|
var link = arr[0];
|
|
|
|
|
|
|
|
if (link.length > letters.length)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for( var i=0; i < link.length; ++i )
|
|
|
|
if ( letters[i] !== link.charAt(i) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
letters = letters.slice(link.length);
|
|
|
|
simpleTrie = arr[1];
|
|
|
|
if (!simpleTrie)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
produceOutput('blocked-hosts-simple-trie', ifProxyBySimpleTrie, 'host', simpleTrie);
|