[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-115.6.0esr-13.5-1] fixup! Bug 40597: Implement TorSettings module



Title: GitLab

Pier Angelo Vendrame pushed to branch tor-browser-115.6.0esr-13.5-1 at The Tor Project / Applications / Tor Browser

Commits:

  • da0f3108
    by Pier Angelo Vendrame at 2024-01-09T18:38:42+01:00
    fixup! Bug 40597: Implement TorSettings module
    
    Bug 42358: Extract the domain fronting request functionality form MoatRPC.
    

3 changed files:

Changes:

  • toolkit/modules/DomainFrontedRequests.sys.mjs
    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
    +const lazy = {};
    
    6
    +
    
    7
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    8
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    9
    +  Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
    
    10
    +  TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    11
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    12
    +  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
    
    13
    +});
    
    14
    +
    
    15
    +/**
    
    16
    + * The meek pluggable transport takes the reflector URL and front domain as
    
    17
    + * proxy credentials, which can be prepared with this function.
    
    18
    + *
    
    19
    + * @param {string} proxyType The proxy type (socks for socks5 or socks4)
    
    20
    + * @param {string} reflector The URL of the service hosted by the CDN
    
    21
    + * @param {string} front The domain to use as a front
    
    22
    + * @returns {string[]} An array containing [username, password]
    
    23
    + */
    
    24
    +function makeMeekCredentials(proxyType, reflector, front) {
    
    25
    +  // Construct the per-connection arguments.
    
    26
    +  let meekClientEscapedArgs = "";
    
    27
    +
    
    28
    +  // Escape aValue per section 3.5 of the PT specification:
    
    29
    +  //   First the "<Key>=<Value>" formatted arguments MUST be escaped,
    
    30
    +  //   such that all backslash, equal sign, and semicolon characters
    
    31
    +  //   are escaped with a backslash.
    
    32
    +  const escapeArgValue = aValue =>
    
    33
    +    aValue
    
    34
    +      ? aValue
    
    35
    +          .replaceAll("\\", "\\\\")
    
    36
    +          .replaceAll("=", "\\=")
    
    37
    +          .replaceAll(";", "\\;")
    
    38
    +      : "";
    
    39
    +
    
    40
    +  if (reflector) {
    
    41
    +    meekClientEscapedArgs += "url="">";
    
    42
    +    meekClientEscapedArgs += escapeArgValue(reflector);
    
    43
    +  }
    
    44
    +
    
    45
    +  if (front) {
    
    46
    +    if (meekClientEscapedArgs.length) {
    
    47
    +      meekClientEscapedArgs += ";";
    
    48
    +    }
    
    49
    +    meekClientEscapedArgs += "front=";
    
    50
    +    meekClientEscapedArgs += escapeArgValue(front);
    
    51
    +  }
    
    52
    +
    
    53
    +  // socks5
    
    54
    +  if (proxyType === "socks") {
    
    55
    +    if (meekClientEscapedArgs.length <= 255) {
    
    56
    +      return [meekClientEscapedArgs, "\x00"];
    
    57
    +    }
    
    58
    +    return [
    
    59
    +      meekClientEscapedArgs.substring(0, 255),
    
    60
    +      meekClientEscapedArgs.substring(255),
    
    61
    +    ];
    
    62
    +  } else if (proxyType === "socks4") {
    
    63
    +    return [meekClientEscapedArgs, undefined];
    
    64
    +  }
    
    65
    +  throw new Error(`Unsupported proxy type ${proxyType}.`);
    
    66
    +}
    
    67
    +
    
    68
    +/**
    
    69
    + * Subprocess-based implementation to launch and control a PT process.
    
    70
    + */
    
    71
    +class MeekTransport {
    
    72
    +  // These members are used by consumers to setup the proxy to do requests over
    
    73
    +  // meek. They are passed to newProxyInfoWithAuth.
    
    74
    +  proxyType = null;
    
    75
    +  proxyAddress = null;
    
    76
    +  proxyPort = 0;
    
    77
    +  proxyUsername = null;
    
    78
    +  proxyPassword = null;
    
    79
    +
    
    80
    +  #inited = false;
    
    81
    +  #meekClientProcess = null;
    
    82
    +
    
    83
    +  // launches the meekprocess
    
    84
    +  async init(reflector, front) {
    
    85
    +    // ensure we haven't already init'd
    
    86
    +    if (this.#inited) {
    
    87
    +      throw new Error("MeekTransport: Already initialized");
    
    88
    +    }
    
    89
    +
    
    90
    +    try {
    
    91
    +      // figure out which pluggable transport to use
    
    92
    +      const supportedTransports = ["meek", "meek_lite"];
    
    93
    +      const provider = await lazy.TorProviderBuilder.build();
    
    94
    +      const proxy = (await provider.getPluggableTransports()).find(
    
    95
    +        pt =>
    
    96
    +          pt.type === "exec" &&
    
    97
    +          supportedTransports.some(t => pt.transports.includes(t))
    
    98
    +      );
    
    99
    +      if (!proxy) {
    
    100
    +        throw new Error("No supported transport found.");
    
    101
    +      }
    
    102
    +
    
    103
    +      const meekTransport = proxy.transports.find(t =>
    
    104
    +        supportedTransports.includes(t)
    
    105
    +      );
    
    106
    +      // Convert meek client path to absolute path if necessary
    
    107
    +      const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
    
    108
    +        "pt-startup-dir",
    
    109
    +        false
    
    110
    +      );
    
    111
    +      if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
    
    112
    +        const meekPath = meekWorkDir.clone();
    
    113
    +        meekPath.appendRelativePath(proxy.pathToBinary);
    
    114
    +        proxy.pathToBinary = meekPath.path;
    
    115
    +      }
    
    116
    +
    
    117
    +      // Setup env and start meek process
    
    118
    +      const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
    
    119
    +      ptStateDir.append("pt_state"); // Match what tor uses.
    
    120
    +
    
    121
    +      const envAdditions = {
    
    122
    +        TOR_PT_MANAGED_TRANSPORT_VER: "1",
    
    123
    +        TOR_PT_STATE_LOCATION: ptStateDir.path,
    
    124
    +        TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
    
    125
    +        TOR_PT_CLIENT_TRANSPORTS: meekTransport,
    
    126
    +      };
    
    127
    +      if (lazy.TorSettings.proxy.enabled) {
    
    128
    +        envAdditions.TOR_PT_PROXY = lazy.TorSettings.proxy.uri;
    
    129
    +      }
    
    130
    +
    
    131
    +      const opts = {
    
    132
    +        command: proxy.pathToBinary,
    
    133
    +        arguments: proxy.options.split(/s+/),
    
    134
    +        workdir: meekWorkDir.path,
    
    135
    +        environmentAppend: true,
    
    136
    +        environment: envAdditions,
    
    137
    +        stderr: "pipe",
    
    138
    +      };
    
    139
    +
    
    140
    +      // Launch meek client
    
    141
    +      this.#meekClientProcess = await lazy.Subprocess.call(opts);
    
    142
    +
    
    143
    +      // Callback chain for reading stderr
    
    144
    +      const stderrLogger = async () => {
    
    145
    +        while (this.#meekClientProcess) {
    
    146
    +          const errString = await this.#meekClientProcess.stderr.readString();
    
    147
    +          if (errString) {
    
    148
    +            console.log(`MeekTransport: stderr => ${errString}`);
    
    149
    +          }
    
    150
    +        }
    
    151
    +      };
    
    152
    +      stderrLogger();
    
    153
    +
    
    154
    +      // Read pt's stdout until terminal (CMETHODS DONE) is reached
    
    155
    +      // returns array of lines for parsing
    
    156
    +      const getInitLines = async (stdout = "") => {
    
    157
    +        stdout += await this.#meekClientProcess.stdout.readString();
    
    158
    +
    
    159
    +        // look for the final message
    
    160
    +        const CMETHODS_DONE = "CMETHODS DONE";
    
    161
    +        let endIndex = stdout.lastIndexOf(CMETHODS_DONE);
    
    162
    +        if (endIndex !== -1) {
    
    163
    +          endIndex += CMETHODS_DONE.length;
    
    164
    +          return stdout.substring(0, endIndex).split("\n");
    
    165
    +        }
    
    166
    +        return getInitLines(stdout);
    
    167
    +      };
    
    168
    +
    
    169
    +      // read our lines from pt's stdout
    
    170
    +      const meekInitLines = await getInitLines();
    
    171
    +      // tokenize our pt lines
    
    172
    +      const meekInitTokens = meekInitLines.map(line => {
    
    173
    +        const tokens = line.split(" ");
    
    174
    +        return {
    
    175
    +          keyword: tokens[0],
    
    176
    +          args: tokens.slice(1),
    
    177
    +        };
    
    178
    +      });
    
    179
    +
    
    180
    +      // parse our pt tokens
    
    181
    +      for (const { keyword, args } of meekInitTokens) {
    
    182
    +        const argsJoined = args.join(" ");
    
    183
    +        let keywordError = false;
    
    184
    +        switch (keyword) {
    
    185
    +          case "VERSION": {
    
    186
    +            if (args.length !== 1 || args[0] !== "1") {
    
    187
    +              keywordError = true;
    
    188
    +            }
    
    189
    +            break;
    
    190
    +          }
    
    191
    +          case "PROXY": {
    
    192
    +            if (args.length !== 1 || args[0] !== "DONE") {
    
    193
    +              keywordError = true;
    
    194
    +            }
    
    195
    +            break;
    
    196
    +          }
    
    197
    +          case "CMETHOD": {
    
    198
    +            if (args.length !== 3) {
    
    199
    +              keywordError = true;
    
    200
    +              break;
    
    201
    +            }
    
    202
    +            const transport = args[0];
    
    203
    +            const proxyType = args[1];
    
    204
    +            const addrPortString = args[2];
    
    205
    +            const addrPort = addrPortString.split(":");
    
    206
    +
    
    207
    +            if (transport !== meekTransport) {
    
    208
    +              throw new Error(
    
    209
    +                `MeekTransport: Expected ${meekTransport} but found ${transport}`
    
    210
    +              );
    
    211
    +            }
    
    212
    +            if (!["socks4", "socks4a", "socks5"].includes(proxyType)) {
    
    213
    +              throw new Error(
    
    214
    +                `MeekTransport: Invalid proxy type => ${proxyType}`
    
    215
    +              );
    
    216
    +            }
    
    217
    +            if (addrPort.length !== 2) {
    
    218
    +              throw new Error(
    
    219
    +                `MeekTransport: Invalid proxy address => ${addrPortString}`
    
    220
    +              );
    
    221
    +            }
    
    222
    +            const addr = addrPort[0];
    
    223
    +            const port = parseInt(addrPort[1]);
    
    224
    +            if (port < 1 || port > 65535) {
    
    225
    +              throw new Error(`MeekTransport: Invalid proxy port => ${port}`);
    
    226
    +            }
    
    227
    +
    
    228
    +            // convert proxy type to strings used by protocol-proxy-servce
    
    229
    +            this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
    
    230
    +            this.proxyAddress = addr;
    
    231
    +            this.proxyPort = port;
    
    232
    +
    
    233
    +            break;
    
    234
    +          }
    
    235
    +          // terminal
    
    236
    +          case "CMETHODS": {
    
    237
    +            if (args.length !== 1 || args[0] !== "DONE") {
    
    238
    +              keywordError = true;
    
    239
    +            }
    
    240
    +            break;
    
    241
    +          }
    
    242
    +          // errors (all fall through):
    
    243
    +          case "VERSION-ERROR":
    
    244
    +          case "ENV-ERROR":
    
    245
    +          case "PROXY-ERROR":
    
    246
    +          case "CMETHOD-ERROR":
    
    247
    +            throw new Error(`MeekTransport: ${keyword} => '${argsJoined}'`);
    
    248
    +        }
    
    249
    +        if (keywordError) {
    
    250
    +          throw new Error(
    
    251
    +            `MeekTransport: Invalid ${keyword} keyword args => '${argsJoined}'`
    
    252
    +          );
    
    253
    +        }
    
    254
    +      }
    
    255
    +
    
    256
    +      // register callback to cleanup on process exit
    
    257
    +      this.#meekClientProcess.wait().then(exitObj => {
    
    258
    +        this.#meekClientProcess = null;
    
    259
    +        this.uninit();
    
    260
    +      });
    
    261
    +      [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    262
    +        this.proxyType,
    
    263
    +        reflector,
    
    264
    +        front
    
    265
    +      );
    
    266
    +      this.#inited = true;
    
    267
    +    } catch (ex) {
    
    268
    +      if (this.#meekClientProcess) {
    
    269
    +        this.#meekClientProcess.kill();
    
    270
    +        this.#meekClientProcess = null;
    
    271
    +      }
    
    272
    +      throw ex;
    
    273
    +    }
    
    274
    +  }
    
    275
    +
    
    276
    +  async uninit() {
    
    277
    +    this.#inited = false;
    
    278
    +
    
    279
    +    await this.#meekClientProcess?.kill();
    
    280
    +    this.#meekClientProcess = null;
    
    281
    +    this.proxyType = null;
    
    282
    +    this.proxyAddress = null;
    
    283
    +    this.proxyPort = 0;
    
    284
    +    this.proxyUsername = null;
    
    285
    +    this.proxyPassword = null;
    
    286
    +  }
    
    287
    +}
    
    288
    +
    
    289
    +/**
    
    290
    + * Android implementation of the Meek process.
    
    291
    + *
    
    292
    + * GeckoView does not provide the subprocess module, so we have to use the
    
    293
    + * EventDispatcher, and have a Java handler start and stop the proxy process.
    
    294
    + */
    
    295
    +class MeekTransportAndroid {
    
    296
    +  // These members are used by consumers to setup the proxy to do requests over
    
    297
    +  // meek. They are passed to newProxyInfoWithAuth.
    
    298
    +  proxyType = null;
    
    299
    +  proxyAddress = null;
    
    300
    +  proxyPort = 0;
    
    301
    +  proxyUsername = null;
    
    302
    +  proxyPassword = null;
    
    303
    +
    
    304
    +  /**
    
    305
    +   * An id for process this instance is linked to.
    
    306
    +   *
    
    307
    +   * Since we do not restrict the transport to be a singleton, we need a handle to
    
    308
    +   * identify the process we want to stop when the transport owner is done.
    
    309
    +   * We use a counter incremented on the Java side for now.
    
    310
    +   *
    
    311
    +   * This number must be a positive integer (i.e., 0 is an invalid handler).
    
    312
    +   *
    
    313
    +   * @type {number}
    
    314
    +   */
    
    315
    +  #id = 0;
    
    316
    +
    
    317
    +  async init(reflector, front) {
    
    318
    +    // ensure we haven't already init'd
    
    319
    +    if (this.#id) {
    
    320
    +      throw new Error("MeekTransport: Already initialized");
    
    321
    +    }
    
    322
    +    const details = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    323
    +      type: "GeckoView:Tor:StartMeek",
    
    324
    +    });
    
    325
    +    this.#id = details.id;
    
    326
    +    this.proxyType = "socks";
    
    327
    +    this.proxyAddress = details.address;
    
    328
    +    this.proxyPort = details.port;
    
    329
    +    [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    330
    +      this.proxyType,
    
    331
    +      reflector,
    
    332
    +      front
    
    333
    +    );
    
    334
    +  }
    
    335
    +
    
    336
    +  async uninit() {
    
    337
    +    lazy.EventDispatcher.instance.sendRequest({
    
    338
    +      type: "GeckoView:Tor:StopMeek",
    
    339
    +      id: this.#id,
    
    340
    +    });
    
    341
    +    this.#id = 0;
    
    342
    +    this.proxyType = null;
    
    343
    +    this.proxyAddress = null;
    
    344
    +    this.proxyPort = 0;
    
    345
    +    this.proxyUsername = null;
    
    346
    +    this.proxyPassword = null;
    
    347
    +  }
    
    348
    +}
    
    349
    +
    
    350
    +/**
    
    351
    + * Callback object to promisify the XPCOM request.
    
    352
    + */
    
    353
    +class ResponseListener {
    
    354
    +  #response = "";
    
    355
    +  #responsePromise;
    
    356
    +  #resolve;
    
    357
    +  #reject;
    
    358
    +  constructor() {
    
    359
    +    this.#response = "";
    
    360
    +    // we need this promise here because await nsIHttpChannel::asyncOpen does
    
    361
    +    // not return only once the request is complete, it seems to return
    
    362
    +    // after it begins, so we have to get the result from this listener object.
    
    363
    +    // This promise is only resolved once onStopRequest is called
    
    364
    +    this.#responsePromise = new Promise((resolve, reject) => {
    
    365
    +      this.#resolve = resolve;
    
    366
    +      this.#reject = reject;
    
    367
    +    });
    
    368
    +  }
    
    369
    +
    
    370
    +  // callers wait on this for final response
    
    371
    +  response() {
    
    372
    +    return this.#responsePromise;
    
    373
    +  }
    
    374
    +
    
    375
    +  // noop
    
    376
    +  onStartRequest(request) {}
    
    377
    +
    
    378
    +  // resolve or reject our Promise
    
    379
    +  onStopRequest(request, status) {
    
    380
    +    try {
    
    381
    +      if (!Components.isSuccessCode(status)) {
    
    382
    +        const errorMessage =
    
    383
    +          lazy.TorLauncherUtil.getLocalizedStringForError(status);
    
    384
    +        this.#reject(new Error(errorMessage));
    
    385
    +      }
    
    386
    +      if (request.responseStatus !== 200) {
    
    387
    +        this.#reject(new Error(request.responseStatusText));
    
    388
    +      }
    
    389
    +    } catch (err) {
    
    390
    +      this.#reject(err);
    
    391
    +    }
    
    392
    +    this.#resolve(this.#response);
    
    393
    +  }
    
    394
    +
    
    395
    +  // read response data
    
    396
    +  onDataAvailable(request, stream, offset, length) {
    
    397
    +    const scriptableStream = Cc[
    
    398
    +      "@mozilla.org/scriptableinputstream;1"
    
    399
    +    ].createInstance(Ci.nsIScriptableInputStream);
    
    400
    +    scriptableStream.init(stream);
    
    401
    +    this.#response += scriptableStream.read(length);
    
    402
    +  }
    
    403
    +}
    
    404
    +
    
    405
    +// constructs the json objects and sends the request over moat
    
    406
    +export class DomainFrontRequestBuilder {
    
    407
    +  #inited = false;
    
    408
    +  #meekTransport = null;
    
    409
    +
    
    410
    +  get inited() {
    
    411
    +    return this.#inited;
    
    412
    +  }
    
    413
    +
    
    414
    +  async init(reflector, front) {
    
    415
    +    if (this.#inited) {
    
    416
    +      throw new Error("MoatRPC: Already initialized");
    
    417
    +    }
    
    418
    +
    
    419
    +    const meekTransport =
    
    420
    +      Services.appinfo.OS === "Android"
    
    421
    +        ? new MeekTransportAndroid()
    
    422
    +        : new MeekTransport();
    
    423
    +    await meekTransport.init(reflector, front);
    
    424
    +    this.#meekTransport = meekTransport;
    
    425
    +    this.#inited = true;
    
    426
    +  }
    
    427
    +
    
    428
    +  async uninit() {
    
    429
    +    await this.#meekTransport?.uninit();
    
    430
    +    this.#meekTransport = null;
    
    431
    +    this.#inited = false;
    
    432
    +  }
    
    433
    +
    
    434
    +  buildHttpHandler(uriString) {
    
    435
    +    if (!this.#inited) {
    
    436
    +      throw new Error("MoatRPC: Not initialized");
    
    437
    +    }
    
    438
    +
    
    439
    +    const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
    
    440
    +      this.#meekTransport;
    
    441
    +
    
    442
    +    const proxyPS = Cc[
    
    443
    +      "@mozilla.org/network/protocol-proxy-service;1"
    
    444
    +    ].getService(Ci.nsIProtocolProxyService);
    
    445
    +    const flags = Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST;
    
    446
    +    const noTimeout = 0xffffffff; // UINT32_MAX
    
    447
    +    const proxyInfo = proxyPS.newProxyInfoWithAuth(
    
    448
    +      proxyType,
    
    449
    +      proxyAddress,
    
    450
    +      proxyPort,
    
    451
    +      proxyUsername,
    
    452
    +      proxyPassword,
    
    453
    +      undefined,
    
    454
    +      undefined,
    
    455
    +      flags,
    
    456
    +      noTimeout,
    
    457
    +      undefined
    
    458
    +    );
    
    459
    +
    
    460
    +    const uri = Services.io.newURI(uriString);
    
    461
    +    // There does not seem to be a way to directly create an nsILoadInfo from
    
    462
    +    // _javascript_, so we create a throw away non-proxied channel to get one.
    
    463
    +    const secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
    
    464
    +    const loadInfo = Services.io.newChannelFromURI(
    
    465
    +      uri,
    
    466
    +      undefined,
    
    467
    +      Services.scriptSecurityManager.getSystemPrincipal(),
    
    468
    +      undefined,
    
    469
    +      secFlags,
    
    470
    +      Ci.nsIContentPolicy.TYPE_OTHER
    
    471
    +    ).loadInfo;
    
    472
    +
    
    473
    +    const httpHandler = Services.io
    
    474
    +      .getProtocolHandler("http")
    
    475
    +      .QueryInterface(Ci.nsIHttpProtocolHandler);
    
    476
    +    const ch = httpHandler
    
    477
    +      .newProxiedChannel(uri, proxyInfo, 0, undefined, loadInfo)
    
    478
    +      .QueryInterface(Ci.nsIHttpChannel);
    
    479
    +
    
    480
    +    // remove all headers except for 'Host"
    
    481
    +    const headers = [];
    
    482
    +    ch.visitRequestHeaders({
    
    483
    +      visitHeader: (key, val) => {
    
    484
    +        if (key !== "Host") {
    
    485
    +          headers.push(key);
    
    486
    +        }
    
    487
    +      },
    
    488
    +    });
    
    489
    +    headers.forEach(key => ch.setRequestHeader(key, "", false));
    
    490
    +
    
    491
    +    return ch;
    
    492
    +  }
    
    493
    +
    
    494
    +  /**
    
    495
    +   * Make a POST request with a JSON body.
    
    496
    +   *
    
    497
    +   * @param {string} url The URL to load
    
    498
    +   * @param {object} args The arguments to send to the procedure. It will be
    
    499
    +   * serialized to JSON by this function and then set as POST body
    
    500
    +   * @returns {Promise<object>} A promise with the parsed response
    
    501
    +   */
    
    502
    +  async buildPostRequest(url, args) {
    
    503
    +    const ch = this.buildHttpHandler(url);
    
    504
    +
    
    505
    +    const argsJson = JSON.stringify(args);
    
    506
    +    const inStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
    
    507
    +      Ci.nsIStringInputStream
    
    508
    +    );
    
    509
    +    inStream.setData(argsJson, argsJson.length);
    
    510
    +    const upChannel = ch.QueryInterface(Ci.nsIUploadChannel);
    
    511
    +    const contentType = "application/vnd.api+json";
    
    512
    +    upChannel.setUploadStream(inStream, contentType, argsJson.length);
    
    513
    +    ch.requestMethod = "POST";
    
    514
    +
    
    515
    +    // Make request
    
    516
    +    const listener = new ResponseListener();
    
    517
    +    await ch.asyncOpen(listener, ch);
    
    518
    +
    
    519
    +    // wait for response
    
    520
    +    const responseJSON = await listener.response();
    
    521
    +
    
    522
    +    // parse that JSON
    
    523
    +    return JSON.parse(responseJSON);
    
    524
    +  }
    
    525
    +}

  • toolkit/modules/Moat.sys.mjs
    ... ... @@ -10,10 +10,8 @@ import {
    10 10
     const lazy = {};
    
    11 11
     
    
    12 12
     ChromeUtils.defineESModuleGetters(lazy, {
    
    13
    -  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    14
    -  Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
    
    15
    -  TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    16
    -  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    13
    +  DomainFrontRequestBuilder:
    
    14
    +    "resource://gre/modules/DomainFrontedRequests.sys.mjs",
    
    17 15
     });
    
    18 16
     
    
    19 17
     const TorLauncherPrefs = Object.freeze({
    
    ... ... @@ -22,372 +20,9 @@ const TorLauncherPrefs = Object.freeze({
    22 20
       moat_service: "extensions.torlauncher.moat_service",
    
    23 21
     });
    
    24 22
     
    
    25
    -function makeMeekCredentials(proxyType) {
    
    26
    -  // Construct the per-connection arguments.
    
    27
    -  let meekClientEscapedArgs = "";
    
    28
    -  const meekReflector = Services.prefs.getStringPref(
    
    29
    -    TorLauncherPrefs.bridgedb_reflector
    
    30
    -  );
    
    31
    -
    
    32
    -  // Escape aValue per section 3.5 of the PT specification:
    
    33
    -  //   First the "<Key>=<Value>" formatted arguments MUST be escaped,
    
    34
    -  //   such that all backslash, equal sign, and semicolon characters
    
    35
    -  //   are escaped with a backslash.
    
    36
    -  const escapeArgValue = aValue =>
    
    37
    -    aValue
    
    38
    -      ? aValue
    
    39
    -          .replaceAll("\\", "\\\\")
    
    40
    -          .replaceAll("=", "\\=")
    
    41
    -          .replaceAll(";", "\\;")
    
    42
    -      : "";
    
    43
    -
    
    44
    -  if (meekReflector) {
    
    45
    -    meekClientEscapedArgs += "url="">";
    
    46
    -    meekClientEscapedArgs += escapeArgValue(meekReflector);
    
    47
    -  }
    
    48
    -  const meekFront = Services.prefs.getStringPref(
    
    49
    -    TorLauncherPrefs.bridgedb_front
    
    50
    -  );
    
    51
    -  if (meekFront) {
    
    52
    -    if (meekClientEscapedArgs.length) {
    
    53
    -      meekClientEscapedArgs += ";";
    
    54
    -    }
    
    55
    -    meekClientEscapedArgs += "front=";
    
    56
    -    meekClientEscapedArgs += escapeArgValue(meekFront);
    
    57
    -  }
    
    58
    -
    
    59
    -  // socks5
    
    60
    -  if (proxyType === "socks") {
    
    61
    -    if (meekClientEscapedArgs.length <= 255) {
    
    62
    -      return [meekClientEscapedArgs, "\x00"];
    
    63
    -    } else {
    
    64
    -      return [
    
    65
    -        meekClientEscapedArgs.substring(0, 255),
    
    66
    -        meekClientEscapedArgs.substring(255),
    
    67
    -      ];
    
    68
    -    }
    
    69
    -    // socks4
    
    70
    -  } else {
    
    71
    -    return [meekClientEscapedArgs, undefined];
    
    72
    -  }
    
    73
    -}
    
    74
    -
    
    75
    -//
    
    76
    -// Launches and controls the PT process lifetime
    
    77
    -//
    
    78
    -class MeekTransport {
    
    79
    -  // These members are used by consumers to setup the proxy to do requests over
    
    80
    -  // meek. They are passed to newProxyInfoWithAuth.
    
    81
    -  proxyType = null;
    
    82
    -  proxyAddress = null;
    
    83
    -  proxyPort = 0;
    
    84
    -  proxyUsername = null;
    
    85
    -  proxyPassword = null;
    
    86
    -
    
    87
    -  #inited = false;
    
    88
    -  #meekClientProcess = null;
    
    89
    -
    
    90
    -  // launches the meekprocess
    
    91
    -  async init() {
    
    92
    -    // ensure we haven't already init'd
    
    93
    -    if (this.#inited) {
    
    94
    -      throw new Error("MeekTransport: Already initialized");
    
    95
    -    }
    
    96
    -
    
    97
    -    try {
    
    98
    -      // figure out which pluggable transport to use
    
    99
    -      const supportedTransports = ["meek", "meek_lite"];
    
    100
    -      const provider = await lazy.TorProviderBuilder.build();
    
    101
    -      const proxy = (await provider.getPluggableTransports()).find(
    
    102
    -        pt =>
    
    103
    -          pt.type === "exec" &&
    
    104
    -          supportedTransports.some(t => pt.transports.includes(t))
    
    105
    -      );
    
    106
    -      if (!proxy) {
    
    107
    -        throw new Error("No supported transport found.");
    
    108
    -      }
    
    109
    -
    
    110
    -      const meekTransport = proxy.transports.find(t =>
    
    111
    -        supportedTransports.includes(t)
    
    112
    -      );
    
    113
    -      // Convert meek client path to absolute path if necessary
    
    114
    -      const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
    
    115
    -        "pt-startup-dir",
    
    116
    -        false
    
    117
    -      );
    
    118
    -      if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
    
    119
    -        const meekPath = meekWorkDir.clone();
    
    120
    -        meekPath.appendRelativePath(proxy.pathToBinary);
    
    121
    -        proxy.pathToBinary = meekPath.path;
    
    122
    -      }
    
    123
    -
    
    124
    -      // Setup env and start meek process
    
    125
    -      const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
    
    126
    -      ptStateDir.append("pt_state"); // Match what tor uses.
    
    127
    -
    
    128
    -      const envAdditions = {
    
    129
    -        TOR_PT_MANAGED_TRANSPORT_VER: "1",
    
    130
    -        TOR_PT_STATE_LOCATION: ptStateDir.path,
    
    131
    -        TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
    
    132
    -        TOR_PT_CLIENT_TRANSPORTS: meekTransport,
    
    133
    -      };
    
    134
    -      if (TorSettings.proxy.enabled) {
    
    135
    -        envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
    
    136
    -      }
    
    137
    -
    
    138
    -      const opts = {
    
    139
    -        command: proxy.pathToBinary,
    
    140
    -        arguments: proxy.options.split(/s+/),
    
    141
    -        workdir: meekWorkDir.path,
    
    142
    -        environmentAppend: true,
    
    143
    -        environment: envAdditions,
    
    144
    -        stderr: "pipe",
    
    145
    -      };
    
    146
    -
    
    147
    -      // Launch meek client
    
    148
    -      this.#meekClientProcess = await lazy.Subprocess.call(opts);
    
    149
    -
    
    150
    -      // Callback chain for reading stderr
    
    151
    -      const stderrLogger = async () => {
    
    152
    -        while (this.#meekClientProcess) {
    
    153
    -          const errString = await this.#meekClientProcess.stderr.readString();
    
    154
    -          if (errString) {
    
    155
    -            console.log(`MeekTransport: stderr => ${errString}`);
    
    156
    -          }
    
    157
    -        }
    
    158
    -      };
    
    159
    -      stderrLogger();
    
    160
    -
    
    161
    -      // Read pt's stdout until terminal (CMETHODS DONE) is reached
    
    162
    -      // returns array of lines for parsing
    
    163
    -      const getInitLines = async (stdout = "") => {
    
    164
    -        stdout += await this.#meekClientProcess.stdout.readString();
    
    165
    -
    
    166
    -        // look for the final message
    
    167
    -        const CMETHODS_DONE = "CMETHODS DONE";
    
    168
    -        let endIndex = stdout.lastIndexOf(CMETHODS_DONE);
    
    169
    -        if (endIndex != -1) {
    
    170
    -          endIndex += CMETHODS_DONE.length;
    
    171
    -          return stdout.substring(0, endIndex).split("\n");
    
    172
    -        }
    
    173
    -        return getInitLines(stdout);
    
    174
    -      };
    
    175
    -
    
    176
    -      // read our lines from pt's stdout
    
    177
    -      const meekInitLines = await getInitLines();
    
    178
    -      // tokenize our pt lines
    
    179
    -      const meekInitTokens = meekInitLines.map(line => {
    
    180
    -        const tokens = line.split(" ");
    
    181
    -        return {
    
    182
    -          keyword: tokens[0],
    
    183
    -          args: tokens.slice(1),
    
    184
    -        };
    
    185
    -      });
    
    186
    -
    
    187
    -      // parse our pt tokens
    
    188
    -      for (const { keyword, args } of meekInitTokens) {
    
    189
    -        const argsJoined = args.join(" ");
    
    190
    -        let keywordError = false;
    
    191
    -        switch (keyword) {
    
    192
    -          case "VERSION": {
    
    193
    -            if (args.length != 1 || args[0] !== "1") {
    
    194
    -              keywordError = true;
    
    195
    -            }
    
    196
    -            break;
    
    197
    -          }
    
    198
    -          case "PROXY": {
    
    199
    -            if (args.length != 1 || args[0] !== "DONE") {
    
    200
    -              keywordError = true;
    
    201
    -            }
    
    202
    -            break;
    
    203
    -          }
    
    204
    -          case "CMETHOD": {
    
    205
    -            if (args.length != 3) {
    
    206
    -              keywordError = true;
    
    207
    -              break;
    
    208
    -            }
    
    209
    -            const transport = args[0];
    
    210
    -            const proxyType = args[1];
    
    211
    -            const addrPortString = args[2];
    
    212
    -            const addrPort = addrPortString.split(":");
    
    213
    -
    
    214
    -            if (transport !== meekTransport) {
    
    215
    -              throw new Error(
    
    216
    -                `MeekTransport: Expected ${meekTransport} but found ${transport}`
    
    217
    -              );
    
    218
    -            }
    
    219
    -            if (!["socks4", "socks4a", "socks5"].includes(proxyType)) {
    
    220
    -              throw new Error(
    
    221
    -                `MeekTransport: Invalid proxy type => ${proxyType}`
    
    222
    -              );
    
    223
    -            }
    
    224
    -            if (addrPort.length != 2) {
    
    225
    -              throw new Error(
    
    226
    -                `MeekTransport: Invalid proxy address => ${addrPortString}`
    
    227
    -              );
    
    228
    -            }
    
    229
    -            const addr = addrPort[0];
    
    230
    -            const port = parseInt(addrPort[1]);
    
    231
    -            if (port < 1 || port > 65535) {
    
    232
    -              throw new Error(`MeekTransport: Invalid proxy port => ${port}`);
    
    233
    -            }
    
    234
    -
    
    235
    -            // convert proxy type to strings used by protocol-proxy-servce
    
    236
    -            this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
    
    237
    -            this.proxyAddress = addr;
    
    238
    -            this.proxyPort = port;
    
    239
    -
    
    240
    -            break;
    
    241
    -          }
    
    242
    -          // terminal
    
    243
    -          case "CMETHODS": {
    
    244
    -            if (args.length != 1 || args[0] !== "DONE") {
    
    245
    -              keywordError = true;
    
    246
    -            }
    
    247
    -            break;
    
    248
    -          }
    
    249
    -          // errors (all fall through):
    
    250
    -          case "VERSION-ERROR":
    
    251
    -          case "ENV-ERROR":
    
    252
    -          case "PROXY-ERROR":
    
    253
    -          case "CMETHOD-ERROR":
    
    254
    -            throw new Error(`MeekTransport: ${keyword} => '${argsJoined}'`);
    
    255
    -        }
    
    256
    -        if (keywordError) {
    
    257
    -          throw new Error(
    
    258
    -            `MeekTransport: Invalid ${keyword} keyword args => '${argsJoined}'`
    
    259
    -          );
    
    260
    -        }
    
    261
    -      }
    
    262
    -
    
    263
    -      // register callback to cleanup on process exit
    
    264
    -      this.#meekClientProcess.wait().then(exitObj => {
    
    265
    -        this.#meekClientProcess = null;
    
    266
    -        this.uninit();
    
    267
    -      });
    
    268
    -      [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    269
    -        this.proxyType
    
    270
    -      );
    
    271
    -      this.#inited = true;
    
    272
    -    } catch (ex) {
    
    273
    -      if (this.#meekClientProcess) {
    
    274
    -        this.#meekClientProcess.kill();
    
    275
    -        this.#meekClientProcess = null;
    
    276
    -      }
    
    277
    -      throw ex;
    
    278
    -    }
    
    279
    -  }
    
    280
    -
    
    281
    -  async uninit() {
    
    282
    -    this.#inited = false;
    
    283
    -
    
    284
    -    await this.#meekClientProcess?.kill();
    
    285
    -    this.#meekClientProcess = null;
    
    286
    -    this.proxyType = null;
    
    287
    -    this.proxyAddress = null;
    
    288
    -    this.proxyPort = 0;
    
    289
    -    this.proxyUsername = null;
    
    290
    -    this.proxyPassword = null;
    
    291
    -  }
    
    292
    -}
    
    293
    -
    
    294
    -class MeekTransportAndroid {
    
    295
    -  // These members are used by consumers to setup the proxy to do requests over
    
    296
    -  // meek. They are passed to newProxyInfoWithAuth.
    
    297
    -  proxyType = null;
    
    298
    -  proxyAddress = null;
    
    299
    -  proxyPort = 0;
    
    300
    -  proxyUsername = null;
    
    301
    -  proxyPassword = null;
    
    302
    -
    
    303
    -  #id = 0;
    
    304
    -
    
    305
    -  async init() {
    
    306
    -    // ensure we haven't already init'd
    
    307
    -    if (this.#id) {
    
    308
    -      throw new Error("MeekTransport: Already initialized");
    
    309
    -    }
    
    310
    -    const details = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    311
    -      type: "GeckoView:Tor:StartMeek",
    
    312
    -    });
    
    313
    -    this.#id = details.id;
    
    314
    -    this.proxyType = "socks";
    
    315
    -    this.proxyAddress = details.address;
    
    316
    -    this.proxyPort = details.port;
    
    317
    -    [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    318
    -      this.proxyType
    
    319
    -    );
    
    320
    -  }
    
    321
    -
    
    322
    -  async uninit() {
    
    323
    -    lazy.EventDispatcher.instance.sendRequest({
    
    324
    -      type: "GeckoView:Tor:StopMeek",
    
    325
    -      id: this.#id,
    
    326
    -    });
    
    327
    -    this.#id = 0;
    
    328
    -    this.proxyType = null;
    
    329
    -    this.proxyAddress = null;
    
    330
    -    this.proxyPort = 0;
    
    331
    -    this.proxyUsername = null;
    
    332
    -    this.proxyPassword = null;
    
    333
    -  }
    
    334
    -}
    
    335
    -
    
    336
    -//
    
    337
    -// Callback object with a cached promise for the returned Moat data
    
    338
    -//
    
    339
    -class MoatResponseListener {
    
    340
    -  #response = "";
    
    341
    -  #responsePromise;
    
    342
    -  #resolve;
    
    343
    -  #reject;
    
    344
    -  constructor() {
    
    345
    -    this.#response = "";
    
    346
    -    // we need this promise here because await nsIHttpChannel::asyncOpen does
    
    347
    -    // not return only once the request is complete, it seems to return
    
    348
    -    // after it begins, so we have to get the result from this listener object.
    
    349
    -    // This promise is only resolved once onStopRequest is called
    
    350
    -    this.#responsePromise = new Promise((resolve, reject) => {
    
    351
    -      this.#resolve = resolve;
    
    352
    -      this.#reject = reject;
    
    353
    -    });
    
    354
    -  }
    
    355
    -
    
    356
    -  // callers wait on this for final response
    
    357
    -  response() {
    
    358
    -    return this.#responsePromise;
    
    359
    -  }
    
    360
    -
    
    361
    -  // noop
    
    362
    -  onStartRequest(request) {}
    
    363
    -
    
    364
    -  // resolve or reject our Promise
    
    365
    -  onStopRequest(request, status) {
    
    366
    -    try {
    
    367
    -      if (!Components.isSuccessCode(status)) {
    
    368
    -        const errorMessage =
    
    369
    -          lazy.TorLauncherUtil.getLocalizedStringForError(status);
    
    370
    -        this.#reject(new Error(errorMessage));
    
    371
    -      }
    
    372
    -      if (request.responseStatus != 200) {
    
    373
    -        this.#reject(new Error(request.responseStatusText));
    
    374
    -      }
    
    375
    -    } catch (err) {
    
    376
    -      this.#reject(err);
    
    377
    -    }
    
    378
    -    this.#resolve(this.#response);
    
    379
    -  }
    
    380
    -
    
    381
    -  // read response data
    
    382
    -  onDataAvailable(request, stream, offset, length) {
    
    383
    -    const scriptableStream = Cc[
    
    384
    -      "@mozilla.org/scriptableinputstream;1"
    
    385
    -    ].createInstance(Ci.nsIScriptableInputStream);
    
    386
    -    scriptableStream.init(stream);
    
    387
    -    this.#response += scriptableStream.read(length);
    
    388
    -  }
    
    389
    -}
    
    390
    -
    
    23
    +/**
    
    24
    + * A special response listener that collects the received headers.
    
    25
    + */
    
    391 26
     class InternetTestResponseListener {
    
    392 27
       #promise;
    
    393 28
       #resolve;
    
    ... ... @@ -436,129 +71,45 @@ class InternetTestResponseListener {
    436 71
       }
    
    437 72
     }
    
    438 73
     
    
    439
    -// constructs the json objects and sends the request over moat
    
    74
    +/**
    
    75
    + * Constructs JSON objects and sends requests over Moat.
    
    76
    + * The documentation about the JSON schemas to use are available at
    
    77
    + * https://gitlab.torproject.org/tpo/anti-censorship/rdsys/-/blob/main/doc/moat.md.
    
    78
    + */
    
    440 79
     export class MoatRPC {
    
    441
    -  #inited = false;
    
    442
    -  #meekTransport = null;
    
    443
    -
    
    444
    -  get inited() {
    
    445
    -    return this.#inited;
    
    446
    -  }
    
    80
    +  #requestBuilder = null;
    
    447 81
     
    
    448 82
       async init() {
    
    449
    -    if (this.#inited) {
    
    450
    -      throw new Error("MoatRPC: Already initialized");
    
    83
    +    if (this.#requestBuilder !== null) {
    
    84
    +      return;
    
    451 85
         }
    
    452 86
     
    
    453
    -    const meekTransport =
    
    454
    -      Services.appinfo.OS === "Android"
    
    455
    -        ? new MeekTransportAndroid()
    
    456
    -        : new MeekTransport();
    
    457
    -    await meekTransport.init();
    
    458
    -    this.#meekTransport = meekTransport;
    
    459
    -    this.#inited = true;
    
    87
    +    const reflector = Services.prefs.getStringPref(
    
    88
    +      TorLauncherPrefs.bridgedb_reflector
    
    89
    +    );
    
    90
    +    const front = Services.prefs.getStringPref(TorLauncherPrefs.bridgedb_front);
    
    91
    +    const builder = new lazy.DomainFrontRequestBuilder();
    
    92
    +    await builder.init(reflector, front);
    
    93
    +    this.#requestBuilder = builder;
    
    460 94
       }
    
    461 95
     
    
    462 96
       async uninit() {
    
    463
    -    await this.#meekTransport?.uninit();
    
    464
    -    this.#meekTransport = null;
    
    465
    -    this.#inited = false;
    
    466
    -  }
    
    467
    -
    
    468
    -  #makeHttpHandler(uriString) {
    
    469
    -    if (!this.#inited) {
    
    470
    -      throw new Error("MoatRPC: Not initialized");
    
    471
    -    }
    
    472
    -
    
    473
    -    const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
    
    474
    -      this.#meekTransport;
    
    475
    -
    
    476
    -    const proxyPS = Cc[
    
    477
    -      "@mozilla.org/network/protocol-proxy-service;1"
    
    478
    -    ].getService(Ci.nsIProtocolProxyService);
    
    479
    -    const flags = Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST;
    
    480
    -    const noTimeout = 0xffffffff; // UINT32_MAX
    
    481
    -    const proxyInfo = proxyPS.newProxyInfoWithAuth(
    
    482
    -      proxyType,
    
    483
    -      proxyAddress,
    
    484
    -      proxyPort,
    
    485
    -      proxyUsername,
    
    486
    -      proxyPassword,
    
    487
    -      undefined,
    
    488
    -      undefined,
    
    489
    -      flags,
    
    490
    -      noTimeout,
    
    491
    -      undefined
    
    492
    -    );
    
    493
    -
    
    494
    -    const uri = Services.io.newURI(uriString);
    
    495
    -    // There does not seem to be a way to directly create an nsILoadInfo from
    
    496
    -    // _javascript_, so we create a throw away non-proxied channel to get one.
    
    497
    -    const secFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
    
    498
    -    const loadInfo = Services.io.newChannelFromURI(
    
    499
    -      uri,
    
    500
    -      undefined,
    
    501
    -      Services.scriptSecurityManager.getSystemPrincipal(),
    
    502
    -      undefined,
    
    503
    -      secFlags,
    
    504
    -      Ci.nsIContentPolicy.TYPE_OTHER
    
    505
    -    ).loadInfo;
    
    506
    -
    
    507
    -    const httpHandler = Services.io
    
    508
    -      .getProtocolHandler("http")
    
    509
    -      .QueryInterface(Ci.nsIHttpProtocolHandler);
    
    510
    -    const ch = httpHandler
    
    511
    -      .newProxiedChannel(uri, proxyInfo, 0, undefined, loadInfo)
    
    512
    -      .QueryInterface(Ci.nsIHttpChannel);
    
    513
    -
    
    514
    -    // remove all headers except for 'Host"
    
    515
    -    const headers = [];
    
    516
    -    ch.visitRequestHeaders({
    
    517
    -      visitHeader: (key, val) => {
    
    518
    -        if (key !== "Host") {
    
    519
    -          headers.push(key);
    
    520
    -        }
    
    521
    -      },
    
    522
    -    });
    
    523
    -    headers.forEach(key => ch.setRequestHeader(key, "", false));
    
    524
    -
    
    525
    -    return ch;
    
    97
    +    await this.#requestBuilder?.uninit();
    
    98
    +    this.#requestBuilder = null;
    
    526 99
       }
    
    527 100
     
    
    528 101
       async #makeRequest(procedure, args) {
    
    529 102
         const procedureURIString = `${Services.prefs.getStringPref(
    
    530 103
           TorLauncherPrefs.moat_service
    
    531 104
         )}/${procedure}`;
    
    532
    -    const ch = this.#makeHttpHandler(procedureURIString);
    
    533
    -
    
    534
    -    // Arrange for the POST data to be sent.
    
    535
    -    const argsJson = JSON.stringify(args);
    
    536
    -
    
    537
    -    const inStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
    
    538
    -      Ci.nsIStringInputStream
    
    539
    -    );
    
    540
    -    inStream.setData(argsJson, argsJson.length);
    
    541
    -    const upChannel = ch.QueryInterface(Ci.nsIUploadChannel);
    
    542
    -    const contentType = "application/vnd.api+json";
    
    543
    -    upChannel.setUploadStream(inStream, contentType, argsJson.length);
    
    544
    -    ch.requestMethod = "POST";
    
    545
    -
    
    546
    -    // Make request
    
    547
    -    const listener = new MoatResponseListener();
    
    548
    -    await ch.asyncOpen(listener, ch);
    
    549
    -
    
    550
    -    // wait for response
    
    551
    -    const responseJSON = await listener.response();
    
    552
    -
    
    553
    -    // parse that JSON
    
    554
    -    return JSON.parse(responseJSON);
    
    105
    +    return this.#requestBuilder.buildPostRequest(procedureURIString, args);
    
    555 106
       }
    
    556 107
     
    
    557 108
       async testInternetConnection() {
    
    558 109
         const uri = `${Services.prefs.getStringPref(
    
    559 110
           TorLauncherPrefs.moat_service
    
    560 111
         )}/circumvention/countries`;
    
    561
    -    const ch = this.#makeHttpHandler(uri);
    
    112
    +    const ch = this.#requestBuilder.buildHttpHandler(uri);
    
    562 113
         ch.requestMethod = "HEAD";
    
    563 114
     
    
    564 115
         const listener = new InternetTestResponseListener();
    
    ... ... @@ -566,10 +117,6 @@ export class MoatRPC {
    566 117
         return listener.status;
    
    567 118
       }
    
    568 119
     
    
    569
    -  //
    
    570
    -  // Moat APIs
    
    571
    -  //
    
    572
    -
    
    573 120
       // Receive a CAPTCHA challenge, takes the following parameters:
    
    574 121
       // - transports: array of transport strings available to us eg: ["obfs4", "meek"]
    
    575 122
       //
    

  • toolkit/modules/moz.build
    ... ... @@ -166,6 +166,7 @@ EXTRA_JS_MODULES += [
    166 166
         "DateTimePickerPanel.sys.mjs",
    
    167 167
         "DeferredTask.sys.mjs",
    
    168 168
         "Deprecated.sys.mjs",
    
    169
    +    "DomainFrontedRequests.sys.mjs",
    
    169 170
         "DragDropFilter.sys.mjs",
    
    170 171
         "E10SUtils.sys.mjs",
    
    171 172
         "EventEmitter.sys.mjs",
    

  • _______________________________________________
    tor-commits mailing list
    tor-commits@xxxxxxxxxxxxxxxxxxxx
    https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits