[tor-commits] [pluggable-transports/snowflake-webext] branch main updated: Add Relay URL support to web proxy

gitolite role git at cupani.torproject.org
Thu Jun 16 17:09:29 UTC 2022


This is an automated email from the git hooks/post-receive script.

shelikhoo pushed a commit to branch main
in repository pluggable-transports/snowflake-webext.

The following commit(s) were added to refs/heads/main by this push:
     new 15768f5  Add Relay URL support to web proxy
15768f5 is described below

commit 15768f50c0ddd68d3ffb815cd532ddbd3d85fd41
Author: Shelikhoo <xiaokangwang at outlook.com>
AuthorDate: Fri May 20 16:37:14 2022 +0100

    Add Relay URL support to web proxy
---
 broker.js    |  3 ++-
 config.js    |  2 ++
 proxypair.js | 16 +++++++++++----
 snowflake.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++-----------
 websocket.js | 26 +++++++++++++++++++++++-
 5 files changed, 94 insertions(+), 18 deletions(-)

diff --git a/broker.js b/broker.js
index d83f03c..e993a48 100644
--- a/broker.js
+++ b/broker.js
@@ -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));
     });
diff --git a/config.js b/config.js
index d05554c..cac8506 100644
--- a/config.js
+++ b/config.js
@@ -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
diff --git a/proxypair.js b/proxypair.js
index 169511b..1d9689e 100644
--- a/proxypair.js
+++ b/proxypair.js
@@ -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;
@@ -82,14 +83,14 @@ class ProxyPair {
     channel.onclose = () => {
       log('WebRTC DataChannel closed.');
       snowflake.ui.setStatus('disconnected by webrtc.');
-      if(this.counted) {
+      if (this.counted) {
         snowflake.ui.decreaseClients();
         this.counted = false;
       }
       this.flush();
       return this.close();
     };
-    channel.onerror = function() {
+    channel.onerror = function () {
       return log('Data channel error!');
     };
     channel.binaryType = "arraybuffer";
@@ -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) {
@@ -125,7 +129,7 @@ class ProxyPair {
     this.relay.onclose = () => {
       log(relay.label + ' closed.');
       snowflake.ui.setStatus('disconnected.');
-      if(this.counted) {
+      if (this.counted) {
         snowflake.ui.decreaseClients();
         this.counted = false;
       }
@@ -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;
diff --git a/snowflake.js b/snowflake.js
index 03b0229..7370f01 100644
--- a/snowflake.js
+++ b/snowflake.js
@@ -68,12 +68,12 @@ class Snowflake {
     }
     this.ui.setStatus(msg);
     //update NAT type
-    console.log("NAT type: "+ this.ui.natType);
+    console.log("NAT type: " + this.ui.natType);
     this.broker.setNATType(this.ui.natType);
     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
@@ -83,14 +83,14 @@ class Snowflake {
           pair.close();
           // increase poll interval
           this.pollInterval =
-                Math.min(this.pollInterval + this.config.pollAdjustment,
-                  this.config.slowestBrokerPollInterval);
+            Math.min(this.pollInterval + this.config.pollAdjustment,
+              this.config.slowestBrokerPollInterval);
           if (clientNAT == "restricted") {
             this.natFailures++;
           }
           // if we fail to connect to a restricted client 3 times in
           // a row, assume we have a restricted NAT
-          if (this.natFailures >= 3){
+          if (this.natFailures >= 3) {
             this.ui.natType = "restricted";
             console.log("Learned NAT type: restricted");
             this.natFailures = 0;
@@ -99,13 +99,13 @@ class Snowflake {
         } else {
           // decrease poll interval
           this.pollInterval =
-                Math.max(this.pollInterval - this.config.pollAdjustment,
-                  this.config.defaultBrokerPollInterval);
+            Math.max(this.pollInterval - this.config.pollAdjustment,
+              this.config.defaultBrokerPollInterval);
           this.natFailures = 0;
         }
         return;
       }), this.config.datachannelTimeout);
-    }, function() {
+    }, function () {
       //on error, close proxy pair
       return pair.close();
     });
@@ -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);
@@ -135,11 +150,11 @@ class Snowflake {
 
   sendAnswer(pair) {
     var fail, next;
-    next = function(sdp) {
+    next = function (sdp) {
       dbg('webrtc: Answer ready.');
       return pair.pc.setLocalDescription(sdp).catch(fail);
     };
-    fail = function() {
+    fail = function () {
       pair.close();
       return dbg('webrtc: Failed to create or set Answer');
     };
@@ -154,7 +169,7 @@ class Snowflake {
     pair = new ProxyPair(this.relayAddr, this.rateLimit, this.config);
     this.proxyPairs.push(pair);
 
-    log('Snowflake IDs: ' + (this.proxyPairs.map(function(p) {
+    log('Snowflake IDs: ' + (this.proxyPairs.map(function (p) {
       return p.id;
     })).join(' | '));
 
@@ -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;
diff --git a/websocket.js b/websocket.js
index da7ba94..fdf470c 100644
--- a/websocket.js
+++ b/websocket.js
@@ -27,7 +27,7 @@ class WS {
       if (!path.match(/^\//)) {
         path = '/' + path;
       }
-      path = path.replace(/[^/]+/, function(m) {
+      path = path.replace(/[^/]+/, function (m) {
         return encodeURIComponent(m);
       });
       parts.push(path);
@@ -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);

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the tor-commits mailing list