Verified Commit 15768f50 authored by shelikhoo's avatar shelikhoo
Browse files

Add Relay URL support to web proxy

parent 07742beb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -67,11 +67,12 @@ class Broker {
      this._xhr = xhr; // Used by spec to fake async Broker interaction
      const clients = Math.floor(numClientsConnected / 8) * 8;
      var data = {
        Version: "1.2",
        Version: "1.3",
        Sid: id,
        Type: this.config.proxyType,
        NAT: this.natType,
        Clients: clients,
        AcceptedRelayPattern: this.config.allowedRelayPattern,
      };
      return this._postRequest(xhr, 'proxy', JSON.stringify(data));
    });
+2 −0
Original line number Diff line number Diff line
@@ -51,3 +51,5 @@ Config.prototype.pcConfig = {
};

Config.PROBEURL = "https://snowflake-broker.freehaven.net:8443/probe";

Config.prototype.allowedRelayPattern="snowflake.torproject.net";
 No newline at end of file
+12 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ class ProxyPair {
    this.onError = this.onError.bind(this);
    this.flush = this.flush.bind(this);

    this.relayURL = undefined;
    this.relayAddr = relayAddr;
    this.rateLimit = rateLimit;
    this.config = config;
@@ -112,7 +113,10 @@ class ProxyPair {
    if (peer_ip != null) {
      params.push(["client_ip", peer_ip]);
    }
    var relay = this.relay = WS.makeWebsocket(this.relayAddr, params);
    var relay = this.relay =
      (this.relayURL === undefined) ?
        WS.makeWebsocket(this.relayAddr, params) :
        WS.makeWebsocketFromURL(this.relayURL, params);
    this.relay.label = 'websocket-relay';
    this.relay.onopen = () => {
      if (this.timer) {
@@ -248,6 +252,10 @@ class ProxyPair {
    return (null !== this.pc) && ('closed' !== this.pc.connectionState);
  }

  setRelayURL(relayURL) {
    this.relayURL = relayURL;
  }

}

ProxyPair.prototype.MAX_BUFFER = 10 * 1024 * 1024;
+53 −12
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@ class Snowflake {
    recv = this.broker.getClientOffer(pair.id, this.proxyPairs.length);
    recv.then((resp) => {
      var clientNAT = resp.NAT;
      if (!this.receiveOffer(pair, resp.Offer)) {
      if (!this.receiveOffer(pair, resp.Offer, resp.RelayURL)) {
        return pair.close();
      }
      //set a timeout for channel creation
@@ -114,9 +114,24 @@ class Snowflake {

  // Receive an SDP offer from some client assigned by the Broker,
  // |pair| - an available ProxyPair.
  receiveOffer(pair, desc) {
  receiveOffer(pair, desc, relayURL) {
    var e, offer, sdp;

    try {
      if (relayURL !== undefined) {
        let relayURLParsed = new URL(relayURL);
        let hostname = relayURLParsed.hostname;
        let protocol = relayURLParsed.protocol;
        if (protocol !== "wss:") {
          log('incorrect relay url protocol');
          return false;
        }
        if (!this.checkRelayPattern(this.config.allowedRelayPattern, hostname)) {
          log('relay url hostname does not match allowed pattern');
          return false;
        }
        pair.setRelayURL(relayURL);
      }
      offer = JSON.parse(desc);
      dbg('Received:\n\n' + offer.sdp + '\n');
      sdp = new RTCSessionDescription(offer);
@@ -182,6 +197,32 @@ class Snowflake {
    return results;
  }

  /**
   * checkRelayPattern match str against patten
   * @param {string} pattern
   * @param {string} str typically a domain name to be checked
   * @return {boolean}
   */
  checkRelayPattern(pattern, str) {
    if (typeof pattern !== "string") {
      throw 'invalid checkRelayPattern input: pattern';
    }
    if (typeof str !== "string") {
      throw 'invalid checkRelayPattern input: str';
    }

    let exactMatch = false;
    if (pattern.charAt(0) === "^") {
      exactMatch = true;
      pattern = pattern.substring(1);
    }

    if (exactMatch) {
      return pattern.localeCompare(str) === 0;
    }
    return str.endsWith(pattern);
  }

}

Snowflake.prototype.relayAddr = null;
+25 −1
Original line number Diff line number Diff line
@@ -54,6 +54,30 @@ class WS {
    return ws;
  }

  /**
   * Creates a websocket connection from a URL and params to override
   * @param {URL|string} url
   * @param {URLSearchParams|string[][]} params
   * @return {WebSocket}
   */
  static makeWebsocketFromURL(url, params) {
    let parsedURL = new URL(url);
    let urlpa = new URLSearchParams(params);
    urlpa.forEach(function (value, key) {
      parsedURL.searchParams.set(key, value);
    });

    let ws = new WebSocket(url);
    /*
    'User agents can use this as a hint for how to handle incoming binary data:
    if the attribute is set to 'blob', it is safe to spool it to disk, and if it
    is set to 'arraybuffer', it is likely more efficient to keep the data in
    memory.'
    */
    ws.binaryType = 'arraybuffer';
    return ws;
  }

  static probeWebsocket(addr) {
    return new Promise((resolve, reject) => {
      const ws = WS.makeWebsocket(addr);