tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProxyPerUserContextManager.sys.mjs (5040B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  ProxyTypes: "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs",
     11 });
     12 
     13 XPCOMUtils.defineLazyServiceGetter(
     14  lazy,
     15  "protocolProxyService",
     16  "@mozilla.org/network/protocol-proxy-service;1",
     17  Ci.nsIProtocolProxyService
     18 );
     19 
     20 const nsIProtocolProxyChannelFilter = ChromeUtils.generateQI([
     21  Ci.nsIProtocolProxyChannelFilter,
     22 ]);
     23 
     24 // The maximum uint32 value.
     25 const PR_UINT32_MAX = 4294967295;
     26 
     27 /**
     28 * A ProxyPerUserContextManager class keeps track of user contexts and their proxy configuration.
     29 */
     30 export class ProxyPerUserContextManager {
     31  #channelProxyFilter;
     32  #proxyFilterRegistered;
     33  #userContextToProxyConfiguration;
     34 
     35  constructor() {
     36    this.#proxyFilterRegistered = false;
     37 
     38    // A map between internal user context ids and proxy configurations.
     39    this.#userContextToProxyConfiguration = new Map();
     40  }
     41 
     42  destroy() {
     43    this.#userContextToProxyConfiguration = new Map();
     44 
     45    this.#unregisterProxyFilter();
     46  }
     47 
     48  /**
     49   * Add proxy configuration for a provided user context id.
     50   *
     51   * @param {string} userContextId
     52   *     Internal user context id.
     53   * @param {Proxy} proxy
     54   *     Proxy configuration.
     55   */
     56  addConfiguration(userContextId, proxy) {
     57    this.#userContextToProxyConfiguration.set(userContextId, proxy);
     58 
     59    this.#registerProxyFilter();
     60  }
     61 
     62  /**
     63   * Delete proxy configuration for a provided user context id.
     64   *
     65   * @param {string} userContextId
     66   *     Internal user context id.
     67   */
     68  deleteConfiguration(userContextId) {
     69    this.#userContextToProxyConfiguration.delete(userContextId);
     70 
     71    this.#unregisterProxyFilter();
     72  }
     73 
     74  #addProxyFilter(proxyFilter, proxySettings) {
     75    const { host, port, type } = proxySettings;
     76 
     77    proxyFilter.onProxyFilterResult(
     78      lazy.protocolProxyService.newProxyInfo(
     79        type,
     80        host,
     81        port,
     82        "" /* aProxyAuthorizationHeader */,
     83        "" /* aConnectionIsolationKey */,
     84        Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST /* aFlags */,
     85        PR_UINT32_MAX /* aFailoverTimeout */,
     86        null /* failover proxy */
     87      )
     88    );
     89  }
     90 
     91  #applyFilter = (channel, defaultProxyInfo, proxyFilter) => {
     92    const originAttributes =
     93      channel.loadInfo && channel.loadInfo.originAttributes;
     94 
     95    if (
     96      this.#userContextToProxyConfiguration.has(originAttributes.userContextId)
     97    ) {
     98      const proxyInfo = this.#userContextToProxyConfiguration.get(
     99        originAttributes.userContextId
    100      );
    101 
    102      if (proxyInfo.proxyType === lazy.ProxyTypes.Direct) {
    103        proxyFilter.onProxyFilterResult(null);
    104 
    105        return;
    106      }
    107 
    108      if (proxyInfo.proxyType === lazy.ProxyTypes.Manual) {
    109        const channelURI = channel.originalURI;
    110        for (const url of proxyInfo.noProxy ?? []) {
    111          if (
    112            (url.startsWith(".") && channelURI.host.endsWith(url)) ||
    113            (!url.startsWith(".") && channelURI.host === url)
    114          ) {
    115            proxyFilter.onProxyFilterResult(defaultProxyInfo);
    116 
    117            // If at least one element from noProxy matches the channel URL no need to check further.
    118            return;
    119          }
    120        }
    121 
    122        if (proxyInfo.httpProxy) {
    123          this.#addProxyFilter(proxyFilter, {
    124            host: proxyInfo.httpProxy,
    125            port: proxyInfo.httpProxyPort,
    126            type: "http",
    127          });
    128        }
    129 
    130        if (proxyInfo.sslProxy) {
    131          this.#addProxyFilter(proxyFilter, {
    132            host: proxyInfo.sslProxy,
    133            port: proxyInfo.sslProxyPort,
    134            type: "https",
    135          });
    136        }
    137 
    138        if (proxyInfo.socksProxy) {
    139          this.#addProxyFilter(proxyFilter, {
    140            host: proxyInfo.socksProxy,
    141            port: proxyInfo.socksProxyPort,
    142            type:
    143              proxyInfo.socksVersion === 5
    144                ? "socks"
    145                : `socks${proxyInfo.socksVersion}`,
    146          });
    147        }
    148 
    149        return;
    150      }
    151    }
    152 
    153    proxyFilter.onProxyFilterResult(defaultProxyInfo);
    154  };
    155 
    156  #registerProxyFilter() {
    157    if (!this.#proxyFilterRegistered) {
    158      this.#proxyFilterRegistered = true;
    159 
    160      this.#channelProxyFilter = {
    161        QueryInterface: nsIProtocolProxyChannelFilter,
    162        applyFilter: this.#applyFilter,
    163      };
    164 
    165      lazy.protocolProxyService.registerChannelFilter(
    166        this.#channelProxyFilter,
    167        0 /* set position `0` to override global filters */
    168      );
    169    }
    170  }
    171 
    172  #unregisterProxyFilter() {
    173    if (
    174      this.#proxyFilterRegistered &&
    175      this.#userContextToProxyConfiguration.size === 0
    176    ) {
    177      this.#proxyFilterRegistered = false;
    178      lazy.protocolProxyService.unregisterChannelFilter(
    179        this.#channelProxyFilter
    180      );
    181      this.#channelProxyFilter = null;
    182    }
    183  }
    184 }