143 lines
6.1 KiB
JavaScript
143 lines
6.1 KiB
JavaScript
/**
|
|
* PAC (Proxy Auto-Config) proxy management.
|
|
* Builds and applies a PAC script that routes virtual host traffic through the
|
|
* CONNECT proxy (127.0.0.1:8442). Re-applies automatically if another extension
|
|
* or system setting overrides the proxy configuration.
|
|
* Depends on: state.js (extensionState, pacConfirmedActive, DEFAULT_CONNECT_PROXY_PORT, DEFAULT_PROXY_PORT)
|
|
* Depends on: logs.js (log, debugLog)
|
|
*
|
|
* In Firefox, proxy.settings requires "Allow in Private Windows" for the extension.
|
|
* When that permission is missing we skip PAC set/clear and notify once.
|
|
*/
|
|
let pacFirefoxPrivateBrowsingBlocked = false;
|
|
let pacFirefoxPrivateBrowsingNotified = false;
|
|
|
|
/** True when running in Firefox (uses proxyType/autoConfigUrl; Chrome uses mode/pacScript). */
|
|
const isFirefox = typeof (browser.runtime && browser.runtime.getBrowserInfo) === 'function';
|
|
|
|
/** Extract unique two-label base domains from virtualHosts array, always including hole.sail */
|
|
function getActiveTlds(virtualHosts) {
|
|
const seen = new Set(['hole.sail']);
|
|
for (const v of (virtualHosts || [])) {
|
|
if (v && v.hostname) {
|
|
const parts = v.hostname.split('.');
|
|
if (parts.length >= 3) seen.add(parts.slice(-2).join('.'));
|
|
}
|
|
}
|
|
return Array.from(seen).map(b => '.' + b);
|
|
}
|
|
|
|
/**
|
|
* Build and apply a PAC script routing all virtual host TLDs through the CONNECT proxy.
|
|
* If `tlds` is omitted, derives the active TLD list from `extensionState.virtualHosts`.
|
|
* @param {string[]} [tlds] - Array of TLD suffixes (e.g. `['.hole.sail', '.hs']`).
|
|
*/
|
|
function applyPAC(tlds) {
|
|
if (!browser.proxy || !browser.proxy.settings) {
|
|
log('applyPAC: SKIPPED - no browser.proxy.settings API');
|
|
return;
|
|
}
|
|
if (pacFirefoxPrivateBrowsingBlocked) {
|
|
debugLog('applyPAC: SKIPPED - Firefox private browsing permission required (user must enable in about:addons)');
|
|
return;
|
|
}
|
|
const proxyPort = extensionState.connectProxyPort ?? DEFAULT_CONNECT_PROXY_PORT ?? extensionState.proxyPort ?? DEFAULT_PROXY_PORT;
|
|
const activeTlds = tlds || getActiveTlds(extensionState.virtualHosts);
|
|
log('applyPAC: setting PAC port=', proxyPort, 'tlds=', activeTlds);
|
|
|
|
const clauses = activeTlds.map(t =>
|
|
` if (dnsDomainIs(host, "${t}")) return "PROXY 127.0.0.1:${proxyPort}";`
|
|
).join('\n');
|
|
const pacData = `function FindProxyForURL(url, host) {\n${clauses}\n return "DIRECT";\n}`;
|
|
|
|
const payload = isFirefox
|
|
? { value: { proxyType: 'autoConfig', autoConfigUrl: 'data:application/javascript;base64,' + btoa(pacData) }, scope: 'regular' }
|
|
: { value: { mode: 'pac_script', pacScript: { data: pacData, mandatory: false } }, scope: 'regular' };
|
|
|
|
browser.proxy.settings.set(
|
|
payload,
|
|
() => {
|
|
if (browser.runtime.lastError) {
|
|
const msg = browser.runtime.lastError.message || '';
|
|
if (msg.includes('private browsing') || msg.includes('private window')) {
|
|
pacFirefoxPrivateBrowsingBlocked = true;
|
|
pacConfirmedActive = false;
|
|
if (!pacFirefoxPrivateBrowsingNotified) {
|
|
pacFirefoxPrivateBrowsingNotified = true;
|
|
log('applyPAC: Firefox requires "Allow in Private Windows" for proxy. Enable it in about:addons for this extension.');
|
|
if (browser.notifications && browser.notifications.create) {
|
|
browser.notifications.create('holesail-pac-private-browsing', {
|
|
type: 'basic',
|
|
title: 'Holesail Browser',
|
|
message: 'To use the proxy in Firefox, enable "Allow in Private Windows" for this extension in about:addons.',
|
|
iconUrl: browser.runtime.getURL ? browser.runtime.getURL('icons/48.png') : undefined,
|
|
}).catch(() => {});
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
log('applyPAC: ERROR:', msg);
|
|
pacConfirmedActive = false;
|
|
return;
|
|
}
|
|
browser.proxy.settings.get({}, (details) => {
|
|
if (browser.runtime.lastError) {
|
|
log('applyPAC: get ERROR:', browser.runtime.lastError.message);
|
|
return;
|
|
}
|
|
const val = details && details.value;
|
|
const mode = val && val.mode;
|
|
const proxyType = val && val.proxyType;
|
|
const loc = details && details.levelOfControl;
|
|
log('applyPAC: mode=', mode, 'proxyType=', proxyType, 'levelOfControl=', loc);
|
|
const isPacActive = (mode === 'pac_script') || (proxyType === 'autoConfig');
|
|
if (loc === 'controlled_by_this_extension' && isPacActive) {
|
|
pacConfirmedActive = true;
|
|
log('applyPAC: ACTIVE');
|
|
} else {
|
|
pacConfirmedActive = false;
|
|
log('applyPAC: WARNING not active - mode=', mode, 'proxyType=', proxyType, 'loc=', loc);
|
|
}
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
// Re-apply PAC whenever proxy settings change (e.g. another extension or system override)
|
|
if (browser.proxy && browser.proxy.settings && browser.proxy.settings.onChange) {
|
|
browser.proxy.settings.onChange.addListener((details) => {
|
|
const loc = details && details.levelOfControl;
|
|
const val = details && details.value;
|
|
const mode = val && val.mode;
|
|
const proxyType = val && val.proxyType;
|
|
log('proxy.settings.onChange: levelOfControl=', loc, 'mode=', mode, 'proxyType=', proxyType);
|
|
if (loc === 'controlled_by_this_extension') {
|
|
const isPacActive = (mode === 'pac_script') || (proxyType === 'autoConfig');
|
|
if (isPacActive) pacConfirmedActive = true;
|
|
return;
|
|
}
|
|
pacConfirmedActive = false;
|
|
log('proxy settings changed externally - reapplying PAC');
|
|
applyPAC();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Clear the PAC proxy setting, reverting to direct connections.
|
|
* Called when the native host disconnects.
|
|
*/
|
|
function clearProxy() {
|
|
debugLog('clearProxy');
|
|
if (pacFirefoxPrivateBrowsingBlocked) return;
|
|
if (!browser.proxy || !browser.proxy.settings) return;
|
|
const value = isFirefox ? { proxyType: 'none' } : { mode: 'direct' };
|
|
browser.proxy.settings.set({ value, scope: 'regular' }).catch(() => {});
|
|
}
|
|
|
|
// Log proxy errors for *.hole.sail to help diagnose issues
|
|
if (browser.proxy && browser.proxy.onError) {
|
|
browser.proxy.onError.addListener((details) => {
|
|
log('Proxy error:', details.error, 'url=', details.url);
|
|
});
|
|
}
|