diff --git a/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js b/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js index be7f5cd..e2d5001 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/1-sync-pac-script-with-pac-provider.js @@ -13,518 +13,526 @@ use chrome.runtime.getBackgroundPage(..), extension.getBackgroundPage is deprecated */ +{ // Private namespace starts. -window.antiCensorRu = { + window.antiCensorRu = { - version: chrome.runtime.getManifest().version, + version: chrome.runtime.getManifest().version, - pacProviders: { - Антизапрет: { - pacUrl: 'https://antizapret.prostovpn.org/proxy.pac', - proxyHosts: ['proxy.antizapret.prostovpn.org'], - proxyIps: { - '195.123.209.38': 'proxy.antizapret.prostovpn.org', - '2a02:27ac::10': 'proxy.antizapret.prostovpn.org' + pacProviders: { + Антизапрет: { + pacUrl: 'https://antizapret.prostovpn.org/proxy.pac', + proxyHosts: ['proxy.antizapret.prostovpn.org'], + proxyIps: { + '195.123.209.38': 'proxy.antizapret.prostovpn.org', + '2a02:27ac::10': 'proxy.antizapret.prostovpn.org' + } + }, + Антиценз: { + pacUrl: 'https://config.anticenz.org/proxy.pac', + proxyHosts: ['gw2.anticenz.org'], + proxyIps: { + '5.196.220.114': 'gw2.anticenz.org' + } + }, + Оба_и_на_свитчах: { + //pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14 + pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15 + proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'], + proxyIps: { + '195.123.209.38': 'proxy.antizapret.prostovpn.org', + '2a02:27ac::10': 'proxy.antizapret.prostovpn.org', + '5.196.220.114': 'gw2.anticenz.org' + } } }, - Антиценз: { - pacUrl: 'https://config.anticenz.org/proxy.pac', - proxyHosts: ['gw2.anticenz.org'], - proxyIps: { - '5.196.220.114': 'gw2.anticenz.org' - } + + _currentPacProviderKey: 'Оба_и_на_свитчах', + + get currentPacProviderKey() { return this._currentPacProviderKey }, + set currentPacProviderKey(newKey) { + + if (newKey && !this.pacProviders[newKey]) + throw new IllegalArgumentException('No provider for key:' + newKey); + this._currentPacProviderKey = newKey; }, - Оба_и_на_свитчах: { - //pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14 - pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15 - proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'], - proxyIps: { - '195.123.209.38': 'proxy.antizapret.prostovpn.org', - '2a02:27ac::10': 'proxy.antizapret.prostovpn.org', - '5.196.220.114': 'gw2.anticenz.org' - } - } - }, - _currentPacProviderKey: 'Оба_и_на_свитчах', + get pacProvider() { return this.pacProviders[this.currentPacProviderKey] }, - get currentPacProviderKey() { return this._currentPacProviderKey }, - set currentPacProviderKey(newKey) { + /* + Is it the first time extension installed? Do something, e.g. initiate PAC sync. + */ + ifFirstInstall: false, + lastPacUpdateStamp: 0, - if (newKey && !this.pacProviders[newKey]) - throw new IllegalArgumentException('No provider for key:' + newKey); - this._currentPacProviderKey = newKey; - }, + _periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта Антизапрет', - get pacProvider() { return this.pacProviders[this.currentPacProviderKey] }, + pushToStorage(cb) { - /* - Is it the first time extension installed? Do something, e.g. initiate PAC sync. - */ - ifFirstInstall: false, - lastPacUpdateStamp: 0, + console.log('Pushing to storage...'); - _periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта Антизапрет', - - pushToStorage(cb) { - - console.log('Pushing to storage...'); - - // Copy only settable properties (except functions). - const onlySettable = {}; - for(const key of Object.keys(this)) { - if (Object.getOwnPropertyDescriptor(this, key).writable && typeof(this[key]) !== 'function') { - onlySettable[key] = this[key]; - } - } - - return chrome.storage.local.clear( - () => chrome.storage.local.set( - onlySettable, - chromified(cb, onlySettable) - ) - ); - - }, - - pullFromStorage(cb) { - - chrome.storage.local.get(null, (storage) => { - - const err = checkChromeError(); - if (!err) { - console.log('Pulled from storage:', storage); - for(const key of Object.keys(storage)) { - this[key] = storage[key]; + // Copy only settable properties (except functions). + const onlySettable = {}; + for(const key of Object.keys(this)) { + if (Object.getOwnPropertyDescriptor(this, key).writable && typeof(this[key]) !== 'function') { + onlySettable[key] = this[key]; } } - console.log('Synced with storage, any callback?', !!cb); - return cb && cb(err, storage); + return chrome.storage.local.clear( + () => chrome.storage.local.set( + onlySettable, + chromified(cb, onlySettable) + ) + ); + + }, + + pullFromStorage(cb) { + + chrome.storage.local.get(null, (storage) => { + + const err = checkChromeError(); + if (!err) { + console.log('Pulled from storage:', storage); + for(const key of Object.keys(storage)) { + this[key] = storage[key]; + } + } + + console.log('Synced with storage, any callback?', !!cb); + return cb && cb(err, storage); + + }); + }, + + syncWithPacProvider(cb) { + + cb = asyncLogGroup('Syncing with PAC provider...', cb); + if (!this.pacProvider) { + return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}}); + } + + const pacSetPromise = new Promise( + (resolve, reject) => setPacScriptFromProvider( + this.pacProvider, + (err, res) => { + + if (!err) { + this.lastPacUpdateStamp = Date.now(); + this.ifFirstInstall = false; + this.setAlarms(); + } + + return resolve([err, res]); + + } + ) + ); + + const ipsPromise = new Promise( + (resolve, reject) => updatePacProxyIps( + this.pacProvider, + (ipsError) => { + + if (ipsError && ipsError.clarification) { + ipsError.clarification.ifNotCritical = true; + } + return resolve([ipsError]); + + } + ) + ); + + Promise.all([pacSetPromise, ipsPromise]).then( + ([[pacErr, pacRes], [ipsErr]]) => { + + if (pacErr && ipsErr) { + return cb(pacErr, pacRes); + } + return cb(pacErr || ipsErr); + this.pushToStorage( + (pushErr) => cb(pacErr || ipsErr || pushErr, pacRes) + ); + + } + ); + + }, + + _pacUpdatePeriodInMinutes: 4*60, + + setAlarms() { + + let nextUpdateMoment = this.lastPacUpdateStamp + this._pacUpdatePeriodInMinutes*60*1000; + const now = Date.now(); + if (nextUpdateMoment < now) { + nextUpdateMoment = now; + } + + console.log( 'Next PAC update is scheduled on', new Date(nextUpdateMoment).toLocaleString('ru-RU') ); + + chrome.alarms.create( + this._periodicUpdateAlarmReason, + { + when: nextUpdateMoment, + periodInMinutes: this._pacUpdatePeriodInMinutes + } + ); + + return nextUpdateMoment === now; // ifAlarmTriggered. May be changed in the future. + + }, + + installPac(key, cb) { + + console.log('Installing PAC'); + if(typeof(key) === 'function') { + cb = key; + key = undefined; + } + + if(key) { + this.currentPacProviderKey = key; + } + + return this.syncWithPacProvider(cb); + + }, + + clearPac(cb) { + + cb = asyncLogGroup('Cearing alarms and PAC...', cb); + chrome.alarms.clearAll( + () => chrome.proxy.settings.clear( + {}, + () => { + + const err = checkChromeError(); + if (err) { + return cb(err); + } + this.currentPacProviderKey = undefined; + return this.pushToStorage(cb); + + } + ) + ); + } + + }; + + // ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE + chrome.storage.local.get(null, (oldStorage) => { + + console.log('Init on storage:', oldStorage); + antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0; + + if (!antiCensorRu.ifFirstInstall) { + // LAUNCH, RELOAD, UPDATE + antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey; + antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; + console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') ); + } + + chrome.alarms.onAlarm.addListener( + (alarm) => { + + if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) { + console.log('Periodic PAC update triggered:', new Date().toLocaleString('ru-RU')); + antiCensorRu.syncWithPacProvider(/* Swallows errors. */); + } + + } + ); + console.log('Alarm listener installed. We won\'t miss any PAC update.'); + + window.addEventListener('online', () => { + + console.log('We are online, checking periodic updates...'); + antiCensorRu.setAlarms(); }); - }, - syncWithPacProvider(cb) { - - cb = asyncLogGroup('Syncing with PAC provider...', cb); - if (!this.pacProvider) { - return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}}); + if (antiCensorRu.ifFirstInstall) { + // INSTALL + console.log('Installing...'); + return chrome.runtime.openOptionsPage(); } - const pacSetPromise = new Promise( - (resolve, reject) => setPacScriptFromProvider( - this.pacProvider, - (err, res) => { - - if (!err) { - this.lastPacUpdateStamp = Date.now(); - this.ifFirstInstall = false; - this.setAlarms(); - } - - return resolve([err, res]); - - } - ) - ); - - const ipsPromise = new Promise( - (resolve, reject) => updatePacProxyIps( - this.pacProvider, - (ipsError) => { - - if (ipsError && ipsError.clarification) { - ipsError.clarification.ifNotCritical = true; - } - return resolve([ipsError]); - - } - ) - ); - - Promise.all([pacSetPromise, ipsPromise]).then( - ([[pacErr, pacRes], [ipsErr]]) => { - - if (pacErr && ipsErr) { - return cb(pacErr, pacRes); - } - return cb(pacErr || ipsErr); - this.pushToStorage( - (pushErr) => cb(pacErr || ipsErr || pushErr, pacRes) - ); - - } - ); - - }, - - _pacUpdatePeriodInMinutes: 4*60, - - setAlarms() { - - let nextUpdateMoment = this.lastPacUpdateStamp + this._pacUpdatePeriodInMinutes*60*1000; - const now = Date.now(); - if (nextUpdateMoment < now) - nextUpdateMoment = now; - - console.log( 'Next PAC update is scheduled on', new Date(nextUpdateMoment).toLocaleString('ru-RU') ); - - chrome.alarms.create( - this._periodicUpdateAlarmReason, - { - when: nextUpdateMoment, - periodInMinutes: this._pacUpdatePeriodInMinutes - } - ); - - return nextUpdateMoment === now; // ifAlarmTriggered. May be changed in the future. - }, - - installPac(key, cb) { - - console.log('Installing PAC'); - if(typeof(key) === 'function') { - cb = key; - key = undefined; + if (!antiCensorRu.pacProvider) { + return console.log('No PAC provider set. Do nothing.'); + /* + In case of UPDATE: + 1. new providers will still be shown. + 2. new version won't be pushed to storage + */ } - if(key) { - this.currentPacProviderKey = key; + /* + 1. There is no way to check that chrome.runtime.onInstalled wasn't fired except timeout. + Otherwise we could put storage migration code only there. + 2. We have to check storage for migration before using it. + Better on each launch then on each pull. + */ + + const ifAlarmTriggered = antiCensorRu.setAlarms(); + + if (antiCensorRu.version === oldStorage.version) { + // LAUNCH, RELOAD, ENABLE + antiCensorRu.pacProviders = oldStorage.pacProviders; + return console.log('Extension launched, reloaded or enabled.'); } - return this.syncWithPacProvider(cb); - - }, - - clearPac(cb) { - - cb = asyncLogGroup('Cearing alarms and PAC...', cb); - chrome.alarms.clearAll( - () => chrome.proxy.settings.clear( - {}, - () => { - - const err = checkChromeError(); - if (err) { - return cb(err); - } - this.currentPacProviderKey = undefined; - return this.pushToStorage(cb); - - } - ) - ); - } - -}; - -// ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE -chrome.storage.local.get(null, (oldStorage) => { - - console.log('Init on storage:', oldStorage); - antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0; - - if (!antiCensorRu.ifFirstInstall) { - // LAUNCH, RELOAD, UPDATE - antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey; - antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; - console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') ); - } - - chrome.alarms.onAlarm.addListener( - (alarm) => { - - if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) { - console.log('Periodic PAC update triggered:', new Date().toLocaleString('ru-RU')); - antiCensorRu.syncWithPacProvider(/* Swallows errors. */); - } + // UPDATE & MIGRATION + console.log('Extension updated.'); + if (!ifAlarmTriggered) { + antiCensorRu.pushToStorage(/* Swallows errors. */); } - ); - console.log('Alarm listener installed. We won\'t miss any PAC update.'); - - window.addEventListener('online', () => { - - console.log('We are online, checking periodic updates...'); - antiCensorRu.setAlarms(); + /* + History of Changes to Storage (Migration Guide) + ----------------------------------------------- + Version 0.0.0.10 + * Added this.version + * PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname} + Version 0.0.0.8-9 + * Changed storage.ifNotInstalled to storage.ifFirstInstall + * Added storage.lastPacUpdateStamp + **/ }); - if (antiCensorRu.ifFirstInstall) { - // INSTALL - console.log('Installing...'); - return chrome.runtime.openOptionsPage(); + function asyncLogGroup(...args) { + + const cb = args.pop() || (() => {}); + console.group.apply(console, args); + return function(...cbArgs) { + + console.groupEnd(); + console.log('Group finished.'); + return cb.apply(this, cbArgs); + + } } - if (!antiCensorRu.pacProvider) { - return console.log('No PAC provider set. Do nothing.'); + function checkChromeError(betterStack) { + + // Chrome API calls your cb in a context different from the point of API method invokation. + const err = chrome.runtime.lastError || chrome.extension.lastError || null; + if (err) { + const args = ['API returned error:', err]; + if (betterStack) { + args.push('\n' + betterStack); + } + console.warn.apply(console, args); + } + return err; + } - /* - 1. There is no way to check that chrome.runtime.onInstalled wasn't fired except timeout. - Otherwise we could put storage migration code only there. - 2. We have to check storage for migration before using it. - Better on each launch then on each pull. - */ + function chromified(cb, ...replaceArgs) { - const ifAlarmTriggered = antiCensorRu.setAlarms(); + const stack = (new Error()).stack; + // Take error first callback and covert it to chrome api callback. + return function(...args) { + + if (replaceArgs.length) { + args = replaceArgs; + } + const err = checkChromeError(stack); + return cb && cb.call(this, err, ...args); + + }; - if (antiCensorRu.version === oldStorage.version) { - // LAUNCH, RELOAD, ENABLE - antiCensorRu.pacProviders = oldStorage.pacProviders; - return console.log('Extension launched, reloaded or enabled.'); } - // UPDATE & MIGRATION - console.log('Extension updated.'); - if (!ifAlarmTriggered) { - updatePacProxyIps( - antiCensorRu.pacProvider, - (ipsError) => ipsError ? console.error('Error updating IPs:', ipsError) : antiCensorRu.pushToStorage(/* Swallows errors. */) + function httpGet(url, cb) { + + const start = Date.now(); + return fetch(url).then( + (res) => { + + const textCb = + (err) => cb && res.text() + .then( (text) => cb(err, text), cb ); + const status = res.status; + if ( !( status >= 200 && status < 300 || status === 304 ) ) { + res.clarification = {message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.'}; + return textCb(res); + } + console.log('GETed with success:', url, Date.now() - start); + return textCb(); + + }, + (err) => { + + err.clarification = {message: 'Что-то не так с сетью, проверьте соединение.'}; + return cb && cb(err); + + } ); } - /* - History of Changes to Storage (Migration Guide) - ----------------------------------------------- - Version 0.0.0.10 - * Added this.version - * PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname} - Version 0.0.0.8-9 - * Changed storage.ifNotInstalled to storage.ifFirstInstall - * Added storage.lastPacUpdateStamp - **/ -}); + function getOneDnsRecord(args, cb) { -function asyncLogGroup(...args) { - - const cb = args.pop() || (() => {}); - console.group.apply(console, args); - return function(...cbArgs) { - - console.groupEnd(); - console.log('Group finished.'); - return cb.apply(this, cbArgs); - - } -} - -function checkChromeError(betterStack) { - - // Chrome API calls your cb in a context different from the point of API method invokation. - const err = chrome.runtime.lastError || chrome.extension.lastError || null; - if (err) { - const args = ['API returned error:', err]; - if (betterStack) { - args.push('\n' + betterStack); + // args: { host:..., type: 'AAAA', filter: ['AAAA'] } + if (!(args.host && args.type && cb)) { + throw new Error('Wrong args:' + host + ',' + type); } - console.warn.apply(console, args); - } - return err; -} + const type2str = { + // https://en.wikipedia.org/wiki/List_of_DNS_record_types + // A, AAAA may be localized (github, e.g.), but you may use ANY + 1: 'A', // IPv4 + 28: 'AAAA', // IPv6 + 2: 'NS', + 5: 'CNAME', // Synonyms, returned by server together with A/AAAA. + 255: 'ANY' // Deprecated on some servers, not recommended + }; -function chromified(cb, ...replaceArgs) { - - const stack = (new Error()).stack; - // Take error first callback and covert it to chrome api callback. - return function(...args) { - - if (replaceArgs.length) { - args = replaceArgs; - } - const err = checkChromeError(stack); - return cb && cb.call(this, err, ...args); - - }; - -} - -function httpGet(url, cb) { - - const start = Date.now(); - return fetch(url).then( - (res) => { - - const textCb = - (err) => cb && res.text() - .then( (text) => cb(err, text), cb ); - const status = res.status; - if ( !( status >= 200 && status < 300 || status === 304 ) ) { - res.clarification = {message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.'}; - return textCb(res); - } - console.log('GETed with success:', url, Date.now() - start); - return textCb(); - - }, - (err) => { - - err.clarification = {message: 'Что-то не так с сетью, проверьте соединение.'}; - return cb && cb(err); - - } - ); -} - -function getOneDnsRecord(args, cb) { - - // args: { host:..., type: 'AAAA', filter: ['AAAA'] } - if (!(args.host && args.type && cb)) { - throw new Error('Wrong args:' + host + ',' + type); - } - - const type2str = { - // https://en.wikipedia.org/wiki/List_of_DNS_record_types - // A, AAAA may be localized (github, e.g.), but you may use ANY - 1: 'A', // IPv4 - 28: 'AAAA', // IPv6 - 2: 'NS', - 5: 'CNAME', // Synonyms, returned by server together with A/AAAA. - 255: 'ANY' // Deprecated on some servers, not recommended - }; - - httpGet( - 'https://dns.google.com/resolve?type=' + args.type + '&name=' + args.host, - (err, res) => { - if (res) { - try { - res = JSON.parse(res); - console.log('Json parsed.'); - if (err || res.Status) { - const msg = ['Answer', 'Comment', 'Status'] - .filter( (prop) => res[ prop ] ) - .map( (prop) => prop + ': ' + JSON.stringify( res[ prop ] ) ) - .join(', \n'); - err.clarification.message += ' Сервер (json): ' + msg; + httpGet( + 'https://dns.google.com/resolve?type=' + args.type + '&name=' + args.host, + (err, res) => { + if (res) { + try { + res = JSON.parse(res); + console.log('Json parsed.'); + if (err || res.Status) { + const msg = ['Answer', 'Comment', 'Status'] + .filter( (prop) => res[ prop ] ) + .map( (prop) => prop + ': ' + JSON.stringify( res[ prop ] ) ) + .join(', \n'); + err.clarification.message += ' Сервер (json): ' + msg; + err.data = err.data || res; + } + else { + res = res.Answer || []; + for (const record of res) { + record.type = type2str[ record.type ]; + } + if ( args.filter ) { + res = res.filter( (record) => args.filter.indexOf( record.type ) > -1 ); + } + } + } + catch(e) { + err = e || err || {clarification:{message:''}}; + err.clarification = err.clarification || { message: '' }; + err.clarification.message += ' Сервер (текст): '+ res; + err.clarification.message.trim(); err.data = err.data || res; } - else { - res = res.Answer || []; - for (const record of res) { - record.type = type2str[ record.type ]; - } - if ( args.filter ) { - res = res.filter( (record) => args.filter.indexOf( record.type ) > -1 ); - } - } - } - catch(e) { - err = e || err || {clarification:{message:''}}; - err.clarification = err.clarification || { message: '' }; - err.clarification.message += ' Сервер (текст): '+ res; - err.clarification.message.trim(); - err.data = err.data || res; } + return cb( err, res ); } - return cb( err, res ); - } - ); -}; - -function getDnsRecords(args, cb) { - - /* - Example of input: - { - // Required - host: 'proxy.navalny.cia.gov', - // Optional - types: { - string: ['A', 'AAAA'], // <- Default. Makes one request per each type. - filter: ['A', 'AAAA'], // <- Default. E.g., you want to get rid of CNAME type from response. - } - } - Exmple of answer from google: - "Answer": - [ - { - "name": "apple.com.", // Always matches name in the Question section - "type": 1, // A - Standard DNS RR type - "TTL": 3599, // Record's time-to-live in seconds - "data": "17.178.96.59" // Data for A - IP address as text - }, - ... - Exmple of output: - The same as google, but types _may be_ canonical strings ('AAAA', 'A') - **/ - - if ( !args.host.length ) { - throw new Error('args.host is required: ' + args.host); - } - args.types = Object.assign({ - string: ['A', 'AAAA'], - filter: ['A', 'AAAA'] - }, args.types); - - const promises = args.types.string.map( - (type) => new Promise( (resolve, reject) => - getOneDnsRecord({ host: args.host, type: type, filter: args.types.filter }, (err, res) => err ? reject(err) : resolve(res) ) - ) - ); - Promise.all(promises).then( (answers) => cb( null, [].concat.apply([], answers) ), cb ); -} - -const getIpDnsRecords = (host, cb) => getDnsRecords({ host: host }, cb); - -function updatePacProxyIps(provider, cb) { - - cb = asyncLogGroup('Getting IP for '+ provider.proxyHosts.join(', ') +'...', cb); - let failure = { - clarification: {message:'Не удалось получить один или несколько IP адресов для прокси-серверов. Иконка для уведомления об обходе блокировок может не отображаться.'}, - errors: {} + ); }; - let i = 0; - provider.proxyHosts.map( - (proxyHost) => getIpDnsRecords( - proxyHost, - (err, records) => { - if (!err) { - provider.proxyIps = provider.proxyIps || {}; - records.forEach( (ans) => provider.proxyIps[ ans.data ] = proxyHost ); - } - else { - failure.errors[proxyHost] = err; + function getDnsRecords(args, cb) { + + /* + Example of input: + { + // Required + host: 'proxy.navalny.cia.gov', + // Optional + types: { + string: ['A', 'AAAA'], // <- Default. Makes one request per each type. + filter: ['A', 'AAAA'], // <- Default. E.g., you want to get rid of CNAME type from response. + } } + Exmple of answer from google: + "Answer": + [ + { + "name": "apple.com.", // Always matches name in the Question section + "type": 1, // A - Standard DNS RR type + "TTL": 3599, // Record's time-to-live in seconds + "data": "17.178.96.59" // Data for A - IP address as text + }, + ... + Exmple of output: + The same as google, but types _may be_ canonical strings ('AAAA', 'A') + **/ - if ( ++i === provider.proxyHosts.length ) { - failure = Object.keys(failure.errors).length ? failure : null; - return cb(failure, provider.proxyIps); - } - } - ) - ); -} - -function setPacScriptFromProvider(provider, cb) { - - cb = asyncLogGroup('Getting pac script from provider...', provider.pacUrl, cb); - - httpGet( - provider.pacUrl, - (err, pacData) => { - - if (err) { - err.clarification = { - message: 'Не удалось скачать PAC-скрипт с адреса: ' + provider.pacUrl + '.', - prev: err.clarification - }; - return cb(err); - } - - const config = { - mode: 'pac_script', - pacScript: { - mandatory: false, - data: pacData - } - }; - console.log('Setting chrome proxy settings...'); - chrome.proxy.settings.set( {value: config}, chromified(cb) ); - + if ( !args.host.length ) { + throw new Error('args.host is required: ' + args.host); } - ); + args.types = Object.assign({ + string: ['A', 'AAAA'], + filter: ['A', 'AAAA'] + }, args.types); + + const promises = args.types.string.map( + (type) => new Promise( (resolve, reject) => + getOneDnsRecord({ host: args.host, type: type, filter: args.types.filter }, (err, res) => err ? reject(err) : resolve(res) ) + ) + ); + Promise.all(promises).then( (answers) => cb( null, [].concat.apply([], answers) ), cb ); + } + + const getIpDnsRecords = (host, cb) => getDnsRecords({ host: host }, cb); + + function updatePacProxyIps(provider, cb) { + + cb = asyncLogGroup('Getting IP for '+ provider.proxyHosts.join(', ') +'...', cb); + let failure = { + clarification: {message:'Не удалось получить один или несколько IP адресов для прокси-серверов. Иконка для уведомления об обходе блокировок может не отображаться.'}, + errors: {} + }; + let i = 0; + provider.proxyHosts.map( + (proxyHost) => getIpDnsRecords( + proxyHost, + (err, records) => { + + if (!err) { + provider.proxyIps = provider.proxyIps || {}; + records.forEach( (ans) => provider.proxyIps[ ans.data ] = proxyHost ); + } + else { + failure.errors[proxyHost] = err; + } + + if ( ++i === provider.proxyHosts.length ) { + failure = Object.keys(failure.errors).length ? failure : null; + return cb(failure, provider.proxyIps); + } + } + ) + ); + } + + function setPacScriptFromProvider(provider, cb) { + + cb = asyncLogGroup('Getting pac script from provider...', provider.pacUrl, cb); + + httpGet( + provider.pacUrl, + (err, pacData) => { + + if (err) { + err.clarification = { + message: 'Не удалось скачать PAC-скрипт с адреса: ' + provider.pacUrl + '.', + prev: err.clarification + }; + return cb(err); + } + + const config = { + mode: 'pac_script', + pacScript: { + mandatory: false, + data: pacData + } + }; + console.log('Setting chrome proxy settings...'); + chrome.proxy.settings.set( {value: config}, chromified(cb) ); + + } + ); + } + } window.addEventListener('error', (err) => { diff --git a/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js b/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js index e347e9c..44c44c2 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/2-block-informer.js @@ -18,9 +18,9 @@ window.chrome.browserAction.setBadgeBackgroundColor({ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! -+function() { +{ - var _tabCallbacks = {}; + const _tabCallbacks = {}; function afterTabUpdated(tabId, cb) { if (_tabCallbacks[tabId]) @@ -42,7 +42,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! } chrome.webRequest.onErrorOccurred.addListener( - requestDetails => + (requestDetails) => isInsideTabWithIp(requestDetails) && ( isProxiedAndInformed(requestDetails) || requestDetails.type === 'main_frame' && ( window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip ) @@ -50,9 +50,9 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! { urls: [''] } ); - chrome.tabs.onRemoved.addListener( tabId => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); + chrome.tabs.onRemoved.addListener( (tabId) => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); - var previousUpdateTitleFinished = Promise.resolve(); + let previousUpdateTitleFinished = Promise.resolve(); function isProxiedAndInformed(requestDetails) { @@ -62,12 +62,12 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! return false; } - var ifMainFrame = requestDetails.type === 'main_frame'; + const ifMainFrame = requestDetails.type === 'main_frame'; previousUpdateTitleFinished = previousUpdateTitleFinished.then( () => new Promise( - resolve => { - var cb = () => updateTitle( requestDetails, resolve ); + (resolve) => { + const cb = () => updateTitle( requestDetails, resolve ); return ifMainFrame ? afterTabUpdated(requestDetails.tabId, cb) : cb(); } ) @@ -79,15 +79,16 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! chrome.browserAction.getTitle( { tabId: requestDetails.tabId }, - title => { - var ifTitleSetAlready = /\n/.test(title); - var proxyHost = window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ]; + (title) => { - var hostname = new URL( requestDetails.url ).hostname; + const ifTitleSetAlready = /\n/.test(title); + const proxyHost = window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ]; - var ifShouldUpdateTitle = false; - var indent = ' '; - var proxyTitle = 'Прокси:'; + const hostname = new URL( requestDetails.url ).hostname; + + let ifShouldUpdateTitle = false; + const indent = ' '; + const proxyTitle = 'Прокси:'; if (!ifTitleSetAlready) { title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost; @@ -98,7 +99,8 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! text: ifMainFrame ? '1' : '%1' }); - } else { + } + else { const hostsProxiesPair = title.split(proxyTitle); if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { @@ -110,10 +112,11 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); ifShouldUpdateTitle = true; - var _cb = cb; + const _cb = cb; cb = () => chrome.browserAction.getBadgeText( {tabId: requestDetails.tabId}, - result => { + (result) => { + chrome.browserAction.setBadgeText( { tabId: requestDetails.tabId, @@ -121,27 +124,30 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! } ); return _cb(); + } ); } } - if (ifShouldUpdateTitle) + if (ifShouldUpdateTitle) { chrome.browserAction.setTitle({ title: title, tabId: requestDetails.tabId }); + } return cb(); + } ); } } chrome.webRequest.onResponseStarted.addListener( - requestDetails => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails), + (requestDetails) => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails), { urls: [''] } ); -}(); +} diff --git a/extensions/chromium/minimalistic-pac-setter/extension/3-context-menus.js b/extensions/chromium/minimalistic-pac-setter/extension/3-context-menus.js index 9875db4..5ccbea3 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/3-context-menus.js +++ b/extensions/chromium/minimalistic-pac-setter/extension/3-context-menus.js @@ -5,7 +5,7 @@ const createMenuLinkEntry = (title, tab2url) => chrome.contextMenus.create({ title: title, contexts: ['browser_action'], - onclick: (menuInfo, tab) => Promise.resolve( tab2url( tab ) ).then( url => chrome.tabs.create({url: url}) ) + onclick: (menuInfo, tab) => Promise.resolve( tab2url( tab ) ).then( (url) => chrome.tabs.create({url: url}) ) }); createMenuLinkEntry( 'Сайт доступен из-за границы? Is up?', (tab) => 'http://isup.me/'+ new URL(tab.url).hostname ); diff --git a/extensions/chromium/minimalistic-pac-setter/extension/manifest.json b/extensions/chromium/minimalistic-pac-setter/extension/manifest.json index b9c0965..a837122 100755 --- a/extensions/chromium/minimalistic-pac-setter/extension/manifest.json +++ b/extensions/chromium/minimalistic-pac-setter/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, - "name": "Обход блокировок Рунета", + "name": "Обход блокировок Рунета 0.15", "description": "Аргументы против цензуры: https://git.io/vEkI9", "version": "0.0.0.15", "icons": {