[meek/master] Remove the legacy XPCOM firefox extension.

commit 1dd467e7d73e37cfa89f782f3a05dd98f20a16cd Author: David Fifield <david@bamsoftware.com> Date: Sun Aug 25 18:57:54 2019 -0600 Remove the legacy XPCOM firefox extension. --- README | 9 +- firefox/chrome.manifest | 5 - firefox/components/main.js | 465 --------------------------------------------- firefox/install.rdf | 21 -- 4 files changed, 2 insertions(+), 498 deletions(-) diff --git a/README b/README index 35568b6..b4efead 100644 --- a/README +++ b/README @@ -35,8 +35,8 @@ with the --helper option pointing at a browser extension that has been set up separately. How it works is meek-client tells the browser what URL to request, the browser requests it and returns the payload to meek-client. The TLS implementation is that of the browser, so it better -blends in with allowed traffic. A browser extensions for Firefox is in -the firefox directory. +blends in with allowed traffic. A browser extension for Firefox is in +the webextension directory. Here is a summary of the programs that appear in subdirectories. @@ -44,11 +44,6 @@ appengine: Reflector web app that runs on Google App Engine. The reflector simply copies requests and responses to an instance of meek-server somewhere. -firefox: -Browser extension for TLS camouflage. Only works with versions of -Firefox below 60.0. Use the extension in the webextension for newer -versions of Firefox. - meek-client: The client transport plugin, run by a censored client. diff --git a/firefox/chrome.manifest b/firefox/chrome.manifest deleted file mode 100644 index ef6596c..0000000 --- a/firefox/chrome.manifest +++ /dev/null @@ -1,5 +0,0 @@ -# https://developer.mozilla.org/en-US/docs/How_to_Build_an_XPCOM_Component_in_... -# https://developer.mozilla.org/en-US/docs/Mozilla/XPCOM/Receiving_startup_not... -component {e7bc2b9c-f454-49f3-a19f-14848a4d871d} components/main.js -contract @bamsoftware.com/meek-http-helper;1 {e7bc2b9c-f454-49f3-a19f-14848a4d871d} -category profile-after-change MeekHTTPHelper @bamsoftware.com/meek-http-helper;1 diff --git a/firefox/components/main.js b/firefox/components/main.js deleted file mode 100644 index 48f0978..0000000 --- a/firefox/components/main.js +++ /dev/null @@ -1,465 +0,0 @@ -// This is an extension that allows external programs to make HTTP requests -// using the browser's networking libraries. -// -// The extension opens a TCP socket listening on localhost on an ephemeral port. -// It writes the port number in a recognizable format to stdout so that a parent -// process can read it and connect. When the extension receives a connection, it -// reads a 4-byte big-endian length field, then tries to read that many bytes of -// data. The data is UTF-8–encoded JSON, having the format -// { -// "method": "POST", -// "url": "https://allowed.example/", -// "header": { -// "Host": "forbidden.example", -// "X-Session-Id": "XXXXXXXXXXX"} -// }, -// "proxy": { -// "type": "http", -// "host": "proxy.example", -// "port": 8080 -// }, -// "body": "...base64..." -// } -// The extension makes the request as commanded. It returns the response to the -// client as a JSON blob, preceded by a 4-byte length as before. If successful, -// the response looks like -// { -// "status": 200, -// "body": "...base64..." -// } -// If there is a network error, the "error" key will be defined. A 404 response -// or similar from the target web server is not considered such an error. -// { -// "error": "NS_ERROR_UNKNOWN_HOST" -// } -// The extension closes the connection after each transaction, and the client -// must reconnect to do another request. - -// https://developer.mozilla.org/en-US/docs/How_to_Build_an_XPCOM_Component_in_... -// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPC... -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - -// https://developer.mozilla.org/en-US/docs/Tools/Browser_Console#Console.jsm -Components.utils.import("resource://gre/modules/Console.jsm"); - -// Everything resides within the MeekHTTPHelper namespace. MeekHTTPHelper is -// also the type from which NSGetFactory is constructed, and it is the top-level -// nsIServerSocketListener. -function MeekHTTPHelper() { - this.wrappedJSObject = this; - this.handlers = []; -} -MeekHTTPHelper.prototype = { - classDescription: "meek HTTP helper component", - classID: Components.ID("{e7bc2b9c-f454-49f3-a19f-14848a4d871d}"), - contractID: "@bamsoftware.com/meek-http-helper;1", - - // https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/XPC... - QueryInterface: XPCOMUtils.generateQI([ - Components.interfaces.nsIObserver, - Components.interfaces.nsIServerSocketListener, - ]), - - // nsIObserver implementation. - observe: function(subject, topic, data) { - if (topic !== "profile-after-change") - return; - - try { - // Flush the preferences to disk so that pref values that were - // updated during startup are not lost, e.g., ones related to - // browser updates. - // We do this before we change the network.proxy.socks_remote_dns - // value since we do not want that change to be permanent. See - // https://bugs.torproject.org/16269. - let prefSvc = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefService); - prefSvc.savePrefFile(null); - - let prefs = Components.classes["@mozilla.org/preferences-service;1"] - .getService(Components.interfaces.nsIPrefBranch); - // Allow unproxied DNS, working around a Tor Browser patch: - // https://bugs.torproject.org/11183#comment:6. - // We set TRANSPARENT_PROXY_RESOLVES_HOST whenever we are asked to - // use a proxy, so name resolution uses the proxy despite this pref. - prefs.setBoolPref("network.proxy.socks_remote_dns", false); - - // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIServer... - let serverSocket = Components.classes["@mozilla.org/network/server-socket;1"] - .createInstance(Components.interfaces.nsIServerSocket); - // Listen on an ephemeral port, loopback only, with default backlog. - serverSocket.init(-1, true, -1); - serverSocket.asyncListen(this); - // This output line is used by a controller program to find out what - // address the helper is listening on. For the dump call to have any - // effect, the pref browser.dom.window.dump.enabled must be true. - dump("meek-http-helper: listen 127.0.0.1:" + serverSocket.port + "\n"); - - // Block forever. - // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Threads#Waiting_fo... - let thread = Components.classes["@mozilla.org/thread-manager;1"].getService().currentThread; - while (true) - thread.processNextEvent(true); - } finally { - let app = Components.classes["@mozilla.org/toolkit/app-startup;1"] - .getService(Components.interfaces.nsIAppStartup); - app.quit(app.eForceQuit); - } - }, - - // nsIServerSocketListener implementation. - onSocketAccepted: function(server, transport) { - // dump("onSocketAccepted " + transport.host + ":" + transport.port + "\n"); - // Stop referencing handlers that are no longer alive. - this.handlers = this.handlers.filter(function(h) { return h.transport.isAlive(); }); - this.handlers.push(new MeekHTTPHelper.LocalConnectionHandler(transport)); - }, - onStopListening: function(server, status) { - // dump("onStopListening status " + status + "\n"); - }, -}; - -// Global variables and functions. - -MeekHTTPHelper.LOCAL_READ_TIMEOUT = 2.0; -MeekHTTPHelper.LOCAL_WRITE_TIMEOUT = 2.0; - -// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIProtoc... -MeekHTTPHelper.proxyProtocolService = Components.classes["@mozilla.org/network/protocol-proxy-service;1"] - .getService(Components.interfaces.nsIProtocolProxyService); - -// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIIOServ... -MeekHTTPHelper.ioService = Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService); -MeekHTTPHelper.httpProtocolHandler = MeekHTTPHelper.ioService.getProtocolHandler("http") - .QueryInterface(Components.interfaces.nsIHttpProtocolHandler); - -// Set the transport to time out at the given absolute deadline. -MeekHTTPHelper.refreshDeadline = function(transport, deadline) { - let timeout; - if (deadline === null) - timeout = 0xffffffff; - else - timeout = Math.max(0.0, Math.ceil((deadline - Date.now()) / 1000.0)); - transport.setTimeout(Components.interfaces.nsISocketTransport.TIMEOUT_READ_WRITE, timeout); -}; - -// Reverse-index the Components.results table. -MeekHTTPHelper.lookupStatus = function(status) { - for (let name in Components.results) { - if (Components.results[name] === status) - return name; - } - return null; -}; - -// Enforce restrictions on what requests we are willing to make. These can -// probably be loosened up. Try and rule out anything unexpected until we -// know we need otherwise. -MeekHTTPHelper.requestOk = function(req) { - if (req.method === undefined) { - dump("req missing \"method\".\n"); - return false; - } - if (req.url === undefined) { - dump("req missing \"url\".\n"); - return false; - } - - if (req.method !== "POST") { - dump("req.method is " + JSON.stringify(req.method) + ", not \"POST\".\n"); - return false; - } - if (!(req.url.startsWith("http://") || req.url.startsWith("https://"))) { - dump("req.url doesn't start with \"http://\" or \"https://\".\n"); - return false; - } - - return true; -}; - -// Return an nsIProxyInfo according to the given specification. Returns null on -// error. -// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIProxyI... -// The specification may look like: -// undefined -// {"type": "http", "host": "example.com", "port": 8080} -// {"type": "socks5", "host": "example.com", "port": 1080} -// {"type": "socks4a", "host": "example.com", "port": 1080} -MeekHTTPHelper.buildProxyInfo = function(spec) { - // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interf... - let flags = Components.interfaces.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST; - if (spec === undefined) { - // "direct"; i.e., no proxy. This is the default. - return MeekHTTPHelper.proxyProtocolService.newProxyInfo("direct", "", 0, flags, 0xffffffff, null); - } else if (spec.type === "http") { - // "http" proxy. Versions of Firefox before 32, and Tor Browser before - // 3.6.2, leak the covert Host header in HTTP proxy CONNECT requests. - // Using an HTTP proxy cannot provide effective obfuscation without such - // a patched Firefox. - // https://bugs.torproject.org/12146 - // https://gitweb.torproject.org/tor-browser.git/commit/?id=e08b91c78d919f66dd5... - // https://bugzilla.mozilla.org/show_bug.cgi?id=1017769 - // https://hg.mozilla.org/mozilla-central/rev/a1f6458800d4 - return MeekHTTPHelper.proxyProtocolService.newProxyInfo("http", spec.host, spec.port, flags, 0xffffffff, null); - } else if (spec.type === "socks5") { - // "socks5" is tor's name. "socks" is XPCOM's name. - return MeekHTTPHelper.proxyProtocolService.newProxyInfo("socks", spec.host, spec.port, flags, 0xffffffff, null); - } else if (spec.type === "socks4a") { - // "socks4a" is tor's name. "socks4" is XPCOM's name. - return MeekHTTPHelper.proxyProtocolService.newProxyInfo("socks4", spec.host, spec.port, flags, 0xffffffff, null); - } - return null; -}; - -// Transmit an HTTP response over the given nsITransport. resp is an object with -// keys perhaps including "status", "body", and "error". -MeekHTTPHelper.sendResponse = function(transport, resp) { - // dump("sendResponse " + JSON.stringify(resp) + "\n"); - let outputStream = transport.openOutputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0); - let output = Components.classes["@mozilla.org/binaryoutputstream;1"] - .createInstance(Components.interfaces.nsIBinaryOutputStream); - output.setOutputStream(outputStream); - - let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - let s = JSON.stringify(resp); - let data = converter.convertToByteArray(s); - - let deadline = Date.now() + MeekHTTPHelper.LOCAL_WRITE_TIMEOUT * 1000; - try { - MeekHTTPHelper.refreshDeadline(transport, deadline); - output.write32(data.length); - MeekHTTPHelper.refreshDeadline(transport, deadline); - output.writeByteArray(data, data.length); - MeekHTTPHelper.refreshDeadline(transport, null); - } finally { - output.close(); - } -}; - -// LocalConnectionHandler handles each new client connection received on the -// socket opened by MeekHTTPHelper. It reads a JSON request, makes the request -// on the Internet, and writes the result back to the socket. Error handling -// happens within callbacks. -MeekHTTPHelper.LocalConnectionHandler = function(transport) { - this.transport = transport; - this.requestReader = null; - this.channel = null; - this.listener = null; - this.readRequest(this.makeRequest.bind(this)); -}; -MeekHTTPHelper.LocalConnectionHandler.prototype = { - readRequest: function(callback) { - this.requestReader = new MeekHTTPHelper.RequestReader(this.transport, callback); - }, - - makeRequest: function(req) { - // dump("makeRequest " + JSON.stringify(req) + "\n"); - if (!MeekHTTPHelper.requestOk(req)) { - MeekHTTPHelper.sendResponse(this.transport, {"error": "request failed validation"}); - return; - } - - // Check what proxy to use, if any. - // dump("using proxy " + JSON.stringify(req.proxy) + "\n"); - let proxyInfo = MeekHTTPHelper.buildProxyInfo(req.proxy); - if (proxyInfo === null) { - MeekHTTPHelper.sendResponse(this.transport, {"error": "can't create nsIProxyInfo from " + JSON.stringify(req.proxy)}); - return; - } - - // Construct an HTTP channel with the given nsIProxyInfo. - // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIHttpCh... - let uri = MeekHTTPHelper.ioService.newURI(req.url, null, null); - this.channel = MeekHTTPHelper.httpProtocolHandler.newProxiedChannel(uri, proxyInfo, 0, null) - .QueryInterface(Components.interfaces.nsIHttpChannel); - // Remove pre-set headers. Firefox's AddStandardRequestHeaders adds - // User-Agent, Accept, Accept-Language, and Accept-Encoding, and perhaps - // others. Just remove all of them. - let headers = []; - // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interf... - // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interf... - this.channel.visitRequestHeaders({visitHeader: function(key, value) { headers.push(key); }}) - for (let i = 0; i < headers.length; i++) { - if (headers[i] !== "Host") - this.channel.setRequestHeader(headers[i], "", false); - } - // Set our own headers. - if (req.header !== undefined) { - for (let key in req.header) { - this.channel.setRequestHeader(key, req.header[key], false); - } - } - if (req.body !== undefined) { - let body = atob(req.body); - let inputStream = Components.classes["@mozilla.org/io/string-input-stream;1"] - .createInstance(Components.interfaces.nsIStringInputStream); - inputStream.setData(body, body.length); - let uploadChannel = this.channel.QueryInterface(Components.interfaces.nsIUploadChannel); - uploadChannel.setUploadStream(inputStream, "application/octet-stream", body.length); - } - // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIUpload... - // says we must set requestMethod after calling setUploadStream. - this.channel.requestMethod = req.method; - this.channel.redirectionLimit = 0; - - this.listener = new MeekHTTPHelper.HttpStreamListener(function(resp) { - MeekHTTPHelper.sendResponse(this.transport, resp); - }.bind(this)); - this.channel.asyncOpen(this.listener, this.channel); - }, -}; - -// RequestReader reads a JSON-encoded request from the given transport, and -// calls the given callback with the request as an argument. In case of error, -// the transport is closed and the callback is not called. -MeekHTTPHelper.RequestReader = function(transport, callback) { - this.transport = transport; - this.callback = callback; - - this.curThread = Components.classes["@mozilla.org/thread-manager;1"].getService().currentThread; - this.inputStream = this.transport.openInputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0); - - this.state = this.STATE_READING_LENGTH; - // Initially size the buffer to read the 4-byte length. - this.buf = new Uint8Array(4); - this.bytesToRead = this.buf.length; - this.deadline = Date.now() + MeekHTTPHelper.LOCAL_READ_TIMEOUT * 1000; - this.asyncWait(); -}; -MeekHTTPHelper.RequestReader.prototype = { - // The onInputStreamReady callback is called for all read events. These - // constants keep track of the state of parsing. - STATE_READING_LENGTH: 1, - STATE_READING_OBJECT: 2, - STATE_DONE: 3, - - // Do an asyncWait and handle the result. - asyncWait: function() { - MeekHTTPHelper.refreshDeadline(this.transport, this.deadline); - this.inputStream.asyncWait(this, 0, 0, this.curThread); - }, - - // nsIInputStreamCallback implementation. - onInputStreamReady: function(inputStream) { - try { - let input = Components.classes["@mozilla.org/binaryinputstream;1"] - .createInstance(Components.interfaces.nsIBinaryInputStream); - input.setInputStream(inputStream); - switch (this.state) { - case this.STATE_READING_LENGTH: - this.doStateReadingLength(input); - break; - case this.STATE_READING_OBJECT: - this.doStateReadingObject(input); - break; - } - if (this.state !== this.STATE_DONE) - this.asyncWait(); - } catch (e) { - this.transport.close(0); - throw e; - } - }, - - // Read into this.buf (up to its capacity) and decrement this.bytesToRead. - readIntoBuf: function(input) { - let n = Math.min(input.available(), this.bytesToRead); - let data = input.readByteArray(n); - this.buf.subarray(this.buf.length - this.bytesToRead).set(data); - this.bytesToRead -= n; - }, - - doStateReadingLength: function(input) { - this.readIntoBuf(input); - if (this.bytesToRead > 0) - return; - - let b = this.buf; - let len = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; - if (len > 1000000) - throw Components.Exception("Object length is too large (" + len + " bytes)", Components.results.NS_ERROR_ILLEGAL_VALUE); - - this.state = this.STATE_READING_OBJECT; - this.buf = new Uint8Array(len); - this.bytesToRead = this.buf.length; - }, - - doStateReadingObject: function(input) { - this.readIntoBuf(input); - if (this.bytesToRead > 0) - return; - - let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"] - .createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - let s = converter.convertFromByteArray(this.buf, this.buf.length); - let req = JSON.parse(s); - - this.state = this.STATE_DONE; - this.buf = null; - this.bytesToRead = 0; - - MeekHTTPHelper.refreshDeadline(this.transport, null); - this.callback(req); - }, -}; - -// HttpStreamListener makes the requested HTTP request and calls the given -// callback with a representation of the response. The "error" key of the return -// value is defined if and only if there was an error. -MeekHTTPHelper.HttpStreamListener = function(callback) { - this.callback = callback; - // This is a list of binary strings that is concatenated in onStopRequest. - this.body = []; - this.length = 0; -}; -// https://developer.mozilla.org/en-US/docs/Creating_Sandboxed_HTTP_Connections -MeekHTTPHelper.HttpStreamListener.prototype = { - // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIReques... - onStartRequest: function(req, context) { - // dump("onStartRequest\n"); - }, - onStopRequest: function(req, context, status) { - // dump("onStopRequest " + status + "\n"); - let resp = {}; - try { - resp.status = context.responseStatus; - } catch (e) { - if (e instanceof Components.interfaces.nsIXPCException - && e.result == Components.results.NS_ERROR_NOT_AVAILABLE) { - // Reading context.responseStatus can fail in this way when - // there is no HTTP response; e.g., when the connection is - // reset. - } - } - if (Components.isSuccessCode(status)) { - resp.body = btoa(this.body.join("")); - } else { - let err = MeekHTTPHelper.lookupStatus(status); - if (err !== null) - resp.error = err; - else - resp.error = "error " + String(status); - } - this.callback(resp); - }, - - // https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIStream... - onDataAvailable: function(request, context, stream, sourceOffset, length) { - // dump("onDataAvailable " + length + " bytes\n"); - this.length += length; - let input = Components.classes["@mozilla.org/binaryinputstream;1"] - .createInstance(Components.interfaces.nsIBinaryInputStream); - input.setInputStream(stream); - this.body.push(String.fromCharCode.apply(null, input.readByteArray(length))); - if (this.length > 1000000) { - request.cancel(Components.results.NS_ERROR_ILLEGAL_VALUE); - return; - } - }, -}; - -let NSGetFactory = XPCOMUtils.generateNSGetFactory([MeekHTTPHelper]); diff --git a/firefox/install.rdf b/firefox/install.rdf deleted file mode 100644 index 6f67ded..0000000 --- a/firefox/install.rdf +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0"?> - -<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"> - <Description about="urn:mozilla:install-manifest"> - <em:id>meek-http-helper@bamsoftware.com</em:id> - <em:version>1.0</em:version> - <em:type>2</em:type> - - <!-- Firefox --> - <em:targetApplication> - <Description> - <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> - <em:minVersion>24.0</em:minVersion> - <em:maxVersion>*</em:maxVersion> - </Description> - </em:targetApplication> - - <em:name>meek HTTP helper</em:name> - <em:description>Makes HTTP requests on behalf of meek-client.</em:description> - </Description> -</RDF>
participants (1)
-
dcf@torproject.org