406 lines
11 KiB
JavaScript
406 lines
11 KiB
JavaScript
/**
|
|
* @fileoverview I2P Proxy Manager
|
|
* Handles proxy configuration and routing for I2P container tabs
|
|
*/
|
|
|
|
// Configuration constants
|
|
const PROXY_CONFIG = {
|
|
MESSAGES: {
|
|
TITLE: chrome.i18n.getMessage("titlePreface"),
|
|
WEB: chrome.i18n.getMessage("webPreface"),
|
|
ROUTER: chrome.i18n.getMessage("routerPreface"),
|
|
MAIL: chrome.i18n.getMessage("mailPreface"),
|
|
TORRENT: chrome.i18n.getMessage("torrentPreface"),
|
|
TUNNEL: chrome.i18n.getMessage("i2ptunnelPreface"),
|
|
IRC: chrome.i18n.getMessage("ircPreface"),
|
|
EXTENSION: chrome.i18n.getMessage("extensionPreface"),
|
|
MUWIRE: chrome.i18n.getMessage("muwirePreface"),
|
|
BOTE: chrome.i18n.getMessage("botePreface"),
|
|
BLOG: chrome.i18n.getMessage("blogPreface"),
|
|
BLOG_PRIVATE: chrome.i18n.getMessage("blogPrefacePrivate"),
|
|
TOR: chrome.i18n.getMessage("torPreface"),
|
|
},
|
|
PORTS: {
|
|
IRC: "7669",
|
|
TOR: "7695",
|
|
BLOG: "8084",
|
|
TORRENT: "7662",
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Network Manager for handling WebRTC and network prediction
|
|
*/
|
|
class NetworkManager {
|
|
/**
|
|
* Initialize network settings
|
|
*/
|
|
static async initialize() {
|
|
try {
|
|
await Promise.all([
|
|
browser.privacy.network.peerConnectionEnabled.set({ value: true }),
|
|
chrome.privacy.network.networkPredictionEnabled.set({ value: false }),
|
|
chrome.privacy.network.webRTCIPHandlingPolicy.set({
|
|
value: "disable_non_proxied_udp",
|
|
}),
|
|
]);
|
|
console.info("Network settings initialized");
|
|
} catch (error) {
|
|
console.error("Network initialization failed:", error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy Manager for handling proxy configuration
|
|
*/
|
|
class ProxyManager {
|
|
constructor() {
|
|
this.proxyConfig = {
|
|
type: proxy_scheme(),
|
|
host: proxy_host(),
|
|
port: proxy_port(),
|
|
};
|
|
|
|
this.initialize();
|
|
}
|
|
|
|
/**
|
|
* Initialize proxy settings
|
|
*/
|
|
async initialize() {
|
|
try {
|
|
await this.setupProxyListener();
|
|
await this.setupStorageListener();
|
|
await this.setupWindowListener();
|
|
console.info("Proxy manager initialized");
|
|
} catch (error) {
|
|
console.error("Proxy initialization failed:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setup proxy request listener
|
|
*/
|
|
async setupProxyListener() {
|
|
browser.proxy.onRequest.addListener(this.handleProxyRequest.bind(this), {
|
|
urls: ["<all_urls>"],
|
|
});
|
|
browser.proxy.onError.addListener(this.handleProxyError.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Setup storage change listener
|
|
*/
|
|
async setupStorageListener() {
|
|
browser.storage.onChanged.addListener(this.handleStorageChange.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Setup window creation listener
|
|
*/
|
|
async setupWindowListener() {
|
|
const platformInfo = await browser.runtime.getPlatformInfo();
|
|
if (browser.windows) {
|
|
browser.windows.onCreated.addListener(
|
|
this.handleWindowCreation.bind(this)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle proxy requests
|
|
* @param {Object} requestDetails Request details
|
|
* @return {Object} Proxy configuration
|
|
*/
|
|
async handleProxyRequest(requestDetails) {
|
|
try {
|
|
// Handle proxy host requests
|
|
if (isProxyHost(requestDetails)) {
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
// Handle extension requests
|
|
if (this.isExtensionRequest(requestDetails)) {
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
// Handle container requests
|
|
if (requestDetails.tabId > 0) {
|
|
return await this.handleContainerRequest(requestDetails);
|
|
}
|
|
|
|
// Handle direct requests
|
|
return await this.handleDirectRequest(requestDetails);
|
|
} catch (error) {
|
|
console.error("Proxy request handling failed:", error);
|
|
return { type: "direct" };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle container-specific proxy requests
|
|
* @param {Object} requestDetails Request details
|
|
* @return {Object} Proxy configuration
|
|
*/
|
|
async handleContainerRequest(requestDetails) {
|
|
try {
|
|
const tab = await browser.tabs.get(requestDetails.tabId);
|
|
const context = await this.getContextIdentity(tab);
|
|
|
|
if (!context) {
|
|
return this.getDefaultProxy();
|
|
}
|
|
|
|
return await this.getContainerProxy(context, requestDetails);
|
|
} catch (error) {
|
|
console.error("Container request handling failed:", error);
|
|
return this.getDefaultProxy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle direct (non-container) proxy requests
|
|
* @param {Object} requestDetails Request details
|
|
* @return {Object} Proxy configuration
|
|
*/
|
|
async handleDirectRequest(requestDetails) {
|
|
try {
|
|
// Check for local service ports first
|
|
if (isLocalHost(requestDetails.url)) {
|
|
const localProxyConfig = this.handleLocalServiceRequest(requestDetails);
|
|
if (localProxyConfig !== undefined) {
|
|
return localProxyConfig;
|
|
}
|
|
}
|
|
|
|
// Handle I2P and proxy hosts
|
|
if (i2pHost(requestDetails)) {
|
|
console.debug("(proxy) Direct I2P host request:", requestDetails.url);
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
if (isProxyHost(requestDetails)) {
|
|
console.debug("(proxy) Direct proxy host request:", requestDetails.url);
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
// Handle router console requests
|
|
if (isRouterHost(requestDetails.url)) {
|
|
console.debug(
|
|
"(proxy) Direct router console request:",
|
|
requestDetails.url
|
|
);
|
|
return null;
|
|
}
|
|
|
|
// RPC request handling
|
|
if (requestDetails.url.includes("rpc")) {
|
|
console.debug("(proxy) Direct RPC request:", requestDetails.url);
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
// Default to direct connection for non-I2P traffic
|
|
console.debug("(proxy) Direct clearnet request:", requestDetails.url);
|
|
return { type: "direct" };
|
|
} catch (error) {
|
|
console.error("Direct request handling failed:", error);
|
|
return { type: "direct" };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle local service port requests
|
|
* @private
|
|
* @param {Object} requestDetails Request details
|
|
* @return {Object|undefined} Proxy configuration or undefined
|
|
*/
|
|
handleLocalServiceRequest(requestDetails) {
|
|
// Local service port mapping
|
|
const LOCAL_SERVICES = {
|
|
[PROXY_CONFIG.PORTS.IRC]: true, // IRC Console
|
|
[PROXY_CONFIG.PORTS.TOR]: true, // Tor Console
|
|
[PROXY_CONFIG.PORTS.BLOG]: true, // Blog Console
|
|
[PROXY_CONFIG.PORTS.TORRENT]: true, // Torrent Console
|
|
};
|
|
|
|
// Extract port from URL
|
|
const url = new URL(requestDetails.url);
|
|
const port = url.port;
|
|
|
|
// Return null proxy for local service ports
|
|
if (LOCAL_SERVICES[port]) {
|
|
console.debug(
|
|
`(proxy) Local service request on port ${port} :`,
|
|
requestDetails.url
|
|
);
|
|
return null;
|
|
}
|
|
|
|
// Let the main proxy logic handle other local requests
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Get container identity
|
|
* @param {Object} tab Browser tab
|
|
* @return {Object} Container context
|
|
*/
|
|
async getContextIdentity(tab) {
|
|
try {
|
|
if (
|
|
tab.cookieStoreId === "firefox-default" ||
|
|
tab.cookieStoreId === "firefox-private"
|
|
) {
|
|
return null;
|
|
}
|
|
return await browser.contextualIdentities.get(tab.cookieStoreId);
|
|
} catch (error) {
|
|
console.error("Context identity lookup failed:", error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get container-specific proxy configuration
|
|
* @param {Object} context Container context
|
|
* @param {Object} requestDetails Request details
|
|
* @return {Object} Proxy configuration
|
|
*/
|
|
async getContainerProxy(context, requestDetails) {
|
|
const proxyMap = {
|
|
[PROXY_CONFIG.MESSAGES.IRC]: () => this.getIRCProxy(requestDetails),
|
|
[PROXY_CONFIG.MESSAGES.TOR]: () => this.getTorProxy(requestDetails),
|
|
[PROXY_CONFIG.MESSAGES.BLOG]: () => this.getBlogProxy(requestDetails),
|
|
[PROXY_CONFIG.MESSAGES.TITLE]: () => this.getMainProxy(requestDetails),
|
|
[PROXY_CONFIG.MESSAGES.ROUTER]: () => this.getRouterProxy(requestDetails),
|
|
[PROXY_CONFIG.MESSAGES.TORRENT]: () =>
|
|
this.getTorrentProxy(requestDetails),
|
|
};
|
|
|
|
const handler = proxyMap[context.name];
|
|
return handler ? await handler() : this.getDefaultProxy();
|
|
}
|
|
|
|
/**
|
|
* Get service-specific proxy configurations
|
|
*/
|
|
getIRCProxy(requestDetails) {
|
|
return !requestDetails.url.includes(PROXY_CONFIG.PORTS.IRC)
|
|
? this.proxyConfig
|
|
: null;
|
|
}
|
|
|
|
getTorProxy(requestDetails) {
|
|
return !requestDetails.url.includes(PROXY_CONFIG.PORTS.TOR)
|
|
? this.proxyConfig
|
|
: null;
|
|
}
|
|
|
|
getBlogProxy(requestDetails) {
|
|
return !requestDetails.url.includes(PROXY_CONFIG.PORTS.BLOG)
|
|
? this.proxyConfig
|
|
: null;
|
|
}
|
|
|
|
getMainProxy(requestDetails) {
|
|
if (
|
|
requestDetails.url.startsWith(
|
|
`http ://${proxy_host()}:${control_port()}/i2psnark/`
|
|
)
|
|
) {
|
|
return null;
|
|
}
|
|
return this.proxyConfig;
|
|
}
|
|
|
|
getRouterProxy(requestDetails) {
|
|
return isRouterHost(requestDetails.url) ? null : this.proxyConfig;
|
|
}
|
|
|
|
getTorrentProxy(requestDetails) {
|
|
return requestDetails.url.includes(PROXY_CONFIG.PORTS.TORRENT)
|
|
? null
|
|
: this.getRouterProxy(requestDetails);
|
|
}
|
|
|
|
/**
|
|
* Get default proxy configuration
|
|
* @return {Object} Default proxy
|
|
*/
|
|
getDefaultProxy() {
|
|
return { type: "direct" };
|
|
}
|
|
|
|
/**
|
|
* Handle proxy errors
|
|
* @param {Error} error Proxy error
|
|
*/
|
|
handleProxyError(error) {
|
|
if (error.includes("Invalid proxy server type")) {
|
|
console.warn("Invalid proxy configuration:", error);
|
|
} else {
|
|
console.error("Proxy error:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle storage changes
|
|
*/
|
|
async handleStorageChange() {
|
|
try {
|
|
await this.updateConfig();
|
|
await this.setupProxyListener();
|
|
} catch (error) {
|
|
console.error("Storage update failed:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle window creation
|
|
*/
|
|
async handleWindowCreation() {
|
|
try {
|
|
await this.updateConfig();
|
|
await this.setupProxyListener();
|
|
} catch (error) {
|
|
console.error("Window creation handling failed:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update proxy configuration
|
|
*/
|
|
async updateConfig() {
|
|
console.info(
|
|
"Updating proxy configuration:",
|
|
`Scheme : ${proxy_scheme()},`,
|
|
`Host : ${proxy_host()},`,
|
|
`Port : ${proxy_port()},`,
|
|
`Control : ${control_host()} :${control_port()}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if request is from extension
|
|
* @param {Object} requestDetails Request details
|
|
* @return {boolean}
|
|
*/
|
|
isExtensionRequest(requestDetails) {
|
|
return requestDetails.originUrl === browser.runtime.getURL("security.html");
|
|
}
|
|
}
|
|
|
|
// Initialize managers
|
|
NetworkManager.initialize();
|
|
const proxyManager = new ProxyManager();
|
|
|
|
// Export for testing
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = {
|
|
NetworkManager,
|
|
ProxyManager,
|
|
PROXY_CONFIG,
|
|
};
|
|
}
|