GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

Commit 71768307 authored by Cecylia Bocovich's avatar Cecylia Bocovich

Make a test connection to determine NAT type

Make a test connection to the symmetrically NAT'd probe site to
determine NAT type. This replaces the previous method of getting
ICE candidates from two different STUN servers and more
definitively determines compatability with a symmetrically NAT'd
client.
parent c5edea3c
Pipeline #1526 passed with stage
in 2 minutes and 57 seconds
......@@ -46,3 +46,5 @@ Config.prototype.pcConfig = {
}
]
};
Config.PROBEURL = "https://snowflake-broker.torproject.net:8443/probe";
......@@ -150,10 +150,13 @@ var debug, snowflake, config, broker, ui, log, dbg, init, update, initNATType, s
initNATType = function() {
this.natType = "unknown";
(function loop(_this) {
Util.checkNATType().then((type) => {
Util.checkNATType(config.datachannelTimeout).then((type) => {
console.log("Setting NAT type: " + type);
_this.natType = type;
}).catch((e) => console.log(e));
}).catch((e) => {
console.log(e);
_this.natType = "unknown";
});
// reset NAT type every 24 hours in case proxy location changed
setTimeout(_this.initNATType, 24 * 60 * 60 * 1000);
})(this);
......
......@@ -13,7 +13,6 @@ class WebExtUI extends UI {
this.onMessage = this.onMessage.bind(this);
this.onDisconnect = this.onDisconnect.bind(this);
this.initStats();
this.initNATType();
chrome.runtime.onConnect.addListener(this.onConnect);
}
......@@ -29,10 +28,13 @@ class WebExtUI extends UI {
initNATType() {
this.natType = "unknown";
(function loop(_this) {
Util.checkNATType().then((type) => {
Util.checkNATType(config.datachannelTimeout).then((type) => {
console.log("Setting NAT type: " + type);
_this.natType = type;
}).catch((e) => console.log(e));
}).catch((e) => {
console.log(e);
_this.natType = "unknown";
});
// reset NAT type every 24 hours in case proxy location changed
setTimeout(_this.initNATType, 24 * 60 * 60 * 1000);
})(this);
......@@ -200,6 +202,7 @@ var debug, snowflake, config, broker, ui, log, dbg, init, update, silenceNotific
snowflake = new Snowflake(config, ui, broker);
log('== snowflake proxy ==');
ui.initToggle();
ui.initNATType();
};
update = function() {
......
/* exported Util, Params, DummyRateLimit */
/* global PeerConnection */
/* global PeerConnection, Config */
/*
A JavaScript WebRTC snowflake proxy
......@@ -21,33 +21,63 @@ class Util {
return navigator.cookieEnabled;
}
// returns a promise that fullfills to "restricted" if the
// mapping is symmetric, and we know it's a restrictive NAT,
// and fullfills to "unknown" if the mapping is not
// symmetric.
static checkNATType() {
// returns a promise that fullfills to "restricted" if we
// fail to make a test connection to a known restricted
// NAT, "unrestricted" if the test connection fails, and
// "unknown" if we fail to reach the probe test server
static checkNATType(timeout) {
return new Promise((fulfill, reject) => {
let port = null;
let open = false;
let pc = new PeerConnection({iceServers: [
{urls: 'stun:stun1.l.google.com:19302'},
{urls: 'stun:stun2.l.google.com:19302'}
{urls: 'stun:stun1.l.google.com:19302'}
]});
pc.createDataChannel("NAT test");
pc.onicecandidate = function(e) {
if (e.candidate) {
let p = Parse.portFromCandidate(e.candidate.candidate);
if (port == null) port = p;
else if (p != null && p != port) fulfill("restricted");
} else { // done parsing candidates
fulfill("unknown");
}
let channel = pc.createDataChannel("NAT test");
channel.onopen = function() {
open = true;
fulfill("unrestricted");
channel.close();
pc.close();
};
pc.createOffer().then((offer) => {
pc.setLocalDescription(offer);
}).catch((e) => {
pc.createOffer()
.then((offer) => pc.setLocalDescription(offer))
.then(() => Util.sendOffer(pc.localDescription))
.then((answer) => pc.setRemoteDescription(JSON.parse(answer)))
.catch((e) => {
console.log(e);
reject("Error checking NAT type");
});
setTimeout(() => {if(!open) fulfill("restricted");}, timeout);
});
}
// Assumes getClientOffer happened, and a WebRTC SDP answer has been generated.
// Sends it back to the broker, which passes it back to the original client.
static sendOffer(offer) {
return new Promise((fulfill, reject) => {
var xhr;
xhr = new XMLHttpRequest();
xhr.timeout = 10 * 1000;
xhr.onreadystatechange = function() {
if (xhr.DONE !== xhr.readyState) {
return;
}
switch (xhr.status) {
case 200:
var response = JSON.parse(xhr.responseText);
return fulfill(response.Answer); // Should contain offer.
default:
console.log('Probe ERROR: Unexpected ' + xhr.status + ' - ' + xhr.statusText);
return reject('Failed to get answer from probe service');
}
};
var data = {"Status": "client match", "Offer": JSON.stringify(offer)};
try {
xhr.open('POST', Config.PROBEURL);
} catch (error) {
console.log('Signaling Server: exception while connecting: ' + error.message);
return reject('unable to connect to signaling server');
}
return xhr.send(JSON.stringify(data));
});
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment