From e67df35defaee544fc08418020d341d29cb051c0 Mon Sep 17 00:00:00 2001 From: Patrick Wang Date: Sun, 1 Sep 2013 03:09:17 +0800 Subject: [PATCH] Bug 784816 - Adding upgradeToSecure() to support startTLS in MozTCPSocket. r=mayhemer sr=sicking --- dom/network/interfaces/nsIDOMTCPSocket.idl | 9 ++- dom/network/interfaces/nsITCPSocketChild.idl | 3 +- dom/network/src/PTCPSocket.ipdl | 1 + dom/network/src/TCPSocket.js | 73 ++++++++++++++++--- dom/network/src/TCPSocketChild.cpp | 7 ++ dom/network/src/TCPSocketParent.cpp | 9 +++ dom/network/src/TCPSocketParent.h | 1 + .../src/TCPSocketParentIntermediary.js | 2 +- 8 files changed, 91 insertions(+), 14 deletions(-) diff --git a/dom/network/interfaces/nsIDOMTCPSocket.idl b/dom/network/interfaces/nsIDOMTCPSocket.idl index 2983ed0627d..f12afa40934 100644 --- a/dom/network/interfaces/nsIDOMTCPSocket.idl +++ b/dom/network/interfaces/nsIDOMTCPSocket.idl @@ -32,7 +32,7 @@ interface nsISocketTransport; // Bug 797561 - Expose a server tcp socket API to web applications -[scriptable, uuid(b7803a0b-4492-45ec-ac7a-3e29f6445fa4)] +[scriptable, uuid(65f6d2c8-4be6-4695-958d-0735e8935289)] interface nsIDOMTCPSocket : nsISupports { /** @@ -44,7 +44,7 @@ interface nsIDOMTCPSocket : nsISupports * @param options An object specifying one or more parameters which * determine the details of the socket. * - * useSSL: true to create an SSL socket. Defaults to false. + * useSecureTransport: true to create an SSL socket. Defaults to false. * * binaryType: "arraybuffer" to use ArrayBuffer * instances in the ondata callback and as the argument @@ -74,6 +74,11 @@ interface nsIDOMTCPSocket : nsISupports nsIDOMTCPServerSocket listen(in unsigned short localPort, [optional] in jsval options, [optional] in unsigned short backlog); + /** + * Enable secure on channel. + */ + void upgradeToSecure(); + /** * The host of this socket object. */ diff --git a/dom/network/interfaces/nsITCPSocketChild.idl b/dom/network/interfaces/nsITCPSocketChild.idl index 1696873ebfb..aa725918e9d 100644 --- a/dom/network/interfaces/nsITCPSocketChild.idl +++ b/dom/network/interfaces/nsITCPSocketChild.idl @@ -8,7 +8,7 @@ interface nsITCPSocketInternal; interface nsIDOMWindow; // Interface to allow the content process socket to reach the IPC bridge. -[scriptable, uuid(edf07a93-36a6-4574-8e23-40f64ab5f596)] +[scriptable, uuid(ada5342d-6d45-4ff1-a7d3-6a4b150d0385)] interface nsITCPSocketChild : nsISupports { // Tell the chrome process to open a corresponding connection with the given parameters @@ -23,6 +23,7 @@ interface nsITCPSocketChild : nsISupports void resume(); void suspend(); void close(); + void startTLS(); /** * Initialize the TCP socket on the child side for IPC. It is called from the child side, diff --git a/dom/network/src/PTCPSocket.ipdl b/dom/network/src/PTCPSocket.ipdl index 538fb9715e4..6e257cc5440 100644 --- a/dom/network/src/PTCPSocket.ipdl +++ b/dom/network/src/PTCPSocket.ipdl @@ -37,6 +37,7 @@ protocol PTCPSocket parent: Open(nsString host, uint16_t port, bool useSSL, nsString binaryType); Data(SendableData data); + StartTLS(); Suspend(); Resume(); Close(); diff --git a/dom/network/src/TCPSocket.js b/dom/network/src/TCPSocket.js index 50564dbe988..fbb7288fd58 100644 --- a/dom/network/src/TCPSocket.js +++ b/dom/network/src/TCPSocket.js @@ -157,6 +157,10 @@ TCPSocket.prototype = { // IPC socket actor _socketBridge: null, + // StartTLS + _waitingForStartTLS: false, + _pendingDataAfterStartTLS: [], + // Public accessors. get readyState() { return this._readyState; @@ -210,19 +214,23 @@ TCPSocket.prototype = { this._onclose = f; }, + _activateTLS: function() { + let securityInfo = this._transport.securityInfo + .QueryInterface(Ci.nsISSLSocketControl); + securityInfo.StartTLS(); + }, + // Helper methods. _createTransport: function ts_createTransport(host, port, sslMode) { - let options, optlen; - if (sslMode) { - options = [sslMode]; - optlen = 1; + let options; + if (sslMode === 'ssl') { + options = ['ssl']; } else { - options = null; - optlen = 0; + options = ['starttls']; } return Cc["@mozilla.org/network/socket-transport-service;1"] .getService(Ci.nsISocketTransportService) - .createTransport(options, optlen, host, port, null); + .createTransport(options, 1, host, port, null); }, _ensureCopying: function ts_ensureCopying() { @@ -248,6 +256,21 @@ TCPSocket.prototype = { if (self._multiplexStream.count) { self._ensureCopying(); } else { + // If we are waiting for initiating starttls, we can begin to + // activate tls now. + if (self._waitingForStartTLS && self._readyState == kOPEN) { + self._activateTLS(); + self._waitingForStartTLS = false; + // If we have pending data, we should send them, or fire + // a drain event if we are waiting for it. + if (self._pendingDataAfterStartTLS.length > 0) { + while (self._pendingDataAfterStartTLS.length) + self._multiplexStream.appendStream(self._pendingDataAfterStartTLS.shift()); + self._ensureCopying(); + return; + } + } + if (self._waitingForDrain) { self._waitingForDrain = false; self.callListener("drain"); @@ -435,7 +458,7 @@ TCPSocket.prototype = { that._host = host; that._port = port; if (options !== undefined) { - if (options.useSSL) { + if (options.useSecureTransport) { that._ssl = 'ssl'; } else { that._ssl = false; @@ -458,7 +481,30 @@ TCPSocket.prototype = { that._initStream(that._binaryType); return that; }, - + + upgradeToSecure: function ts_upgradeToSecure() { + if (this._readyState !== kOPEN) { + throw new Error("Socket not open."); + } + if (this._ssl == 'ssl') { + // Already SSL + return; + } + + this._ssl = 'ssl'; + + if (this._inChild) { + this._socketBridge.startTLS(); + return; + } + + if (this._multiplexStream.count == 0) { + this._activateTLS(); + } else { + this._waitingForStartTLS = true; + } + }, + listen: function ts_listen(localPort, options, backlog) { if (!this.initWindowless()) return null; @@ -524,7 +570,14 @@ TCPSocket.prototype = { new_stream = new StringInputStream(); new_stream.setData(data, length); } - this._multiplexStream.appendStream(new_stream); + + if (this._waitingForStartTLS) { + // When we are waiting for starttls, new_stream is added to pendingData + // and will be appended to multiplexStream after tls had been set up. + this._pendingDataAfterStartTLS.push(new_stream); + } else { + this._multiplexStream.appendStream(new_stream); + } if (newBufferedAmount >= BUFFER_SIZE) { // If we buffered more than some arbitrary amount of data, diff --git a/dom/network/src/TCPSocketChild.cpp b/dom/network/src/TCPSocketChild.cpp index 8ee234b205b..9a3576ccf64 100644 --- a/dom/network/src/TCPSocketChild.cpp +++ b/dom/network/src/TCPSocketChild.cpp @@ -158,6 +158,13 @@ TCPSocketChild::RecvCallback(const nsString& aType, return true; } +NS_IMETHODIMP +TCPSocketChild::StartTLS() +{ + SendStartTLS(); + return NS_OK; +} + NS_IMETHODIMP TCPSocketChild::Suspend() { diff --git a/dom/network/src/TCPSocketParent.cpp b/dom/network/src/TCPSocketParent.cpp index 6de19a3f907..3ac96ea38dd 100644 --- a/dom/network/src/TCPSocketParent.cpp +++ b/dom/network/src/TCPSocketParent.cpp @@ -115,6 +115,15 @@ TCPSocketParent::InitJS(const JS::Value& aIntermediary, JSContext* aCx) return NS_OK; } +bool +TCPSocketParent::RecvStartTLS() +{ + NS_ENSURE_TRUE(mSocket, true); + nsresult rv = mSocket->UpgradeToSecure(); + NS_ENSURE_SUCCESS(rv, true); + return true; +} + bool TCPSocketParent::RecvSuspend() { diff --git a/dom/network/src/TCPSocketParent.h b/dom/network/src/TCPSocketParent.h index ffc02c8403d..d3ad9fd3128 100644 --- a/dom/network/src/TCPSocketParent.h +++ b/dom/network/src/TCPSocketParent.h @@ -47,6 +47,7 @@ public: virtual bool RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& useSSL, const nsString& aBinaryType); + virtual bool RecvStartTLS() MOZ_OVERRIDE; virtual bool RecvSuspend() MOZ_OVERRIDE; virtual bool RecvResume() MOZ_OVERRIDE; virtual bool RecvClose() MOZ_OVERRIDE; diff --git a/dom/network/src/TCPSocketParentIntermediary.js b/dom/network/src/TCPSocketParentIntermediary.js index 96cfa5e459a..00e2c4c0736 100644 --- a/dom/network/src/TCPSocketParentIntermediary.js +++ b/dom/network/src/TCPSocketParentIntermediary.js @@ -32,7 +32,7 @@ TCPSocketParentIntermediary.prototype = { open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType) { let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket); - let socket = baseSocket.open(aHost, aPort, {useSSL: aUseSSL, binaryType: aBinaryType}); + let socket = baseSocket.open(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType}); if (!socket) return null;