|
@ -3,14 +3,15 @@ var events = require('events'); |
|
|
var stream = require('stream'); |
|
|
var stream = require('stream'); |
|
|
var assert = process.assert; |
|
|
var assert = process.assert; |
|
|
|
|
|
|
|
|
var debugLevel = parseInt(process.env.NODE_DEBUG, 16); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var debugLevel = parseInt(process.env.NODE_DEBUG, 16); |
|
|
function debug () { |
|
|
function debug () { |
|
|
if (debugLevel & 0x2) { |
|
|
if (debugLevel & 0x2) { |
|
|
util.error.apply(this, arguments); |
|
|
util.error.apply(this, arguments); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Lazy Loaded crypto object */ |
|
|
/* Lazy Loaded crypto object */ |
|
|
var SecureStream = null; |
|
|
var SecureStream = null; |
|
|
|
|
|
|
|
@ -69,24 +70,24 @@ function SecurePair(credentials, isServer) { |
|
|
this.encrypted = new stream.Stream(); |
|
|
this.encrypted = new stream.Stream(); |
|
|
this.encrypted.readable = true; |
|
|
this.encrypted.readable = true; |
|
|
|
|
|
|
|
|
this.cleartext.write = function(data) { |
|
|
this.cleartext.write = function (data) { |
|
|
debug('clearIn data'); |
|
|
debug('clearIn data'); |
|
|
self._clearInPending.push(data); |
|
|
self._clearInPending.push(data); |
|
|
self._cycle(); |
|
|
self._cycle(); |
|
|
return self._cleartextWriteState; |
|
|
return self._cleartextWriteState; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.cleartext.pause = function() { |
|
|
this.cleartext.pause = function () { |
|
|
debug('paused cleartext'); |
|
|
debug('paused cleartext'); |
|
|
self._cleartextWriteState = false; |
|
|
self._cleartextWriteState = false; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.cleartext.resume = function() { |
|
|
this.cleartext.resume = function () { |
|
|
debug('resumed cleartext'); |
|
|
debug('resumed cleartext'); |
|
|
self._cleartextWriteState = true; |
|
|
self._cleartextWriteState = true; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.cleartext.end = function(err) { |
|
|
this.cleartext.end = function (err) { |
|
|
debug('cleartext end'); |
|
|
debug('cleartext end'); |
|
|
if (!self._done) { |
|
|
if (!self._done) { |
|
|
self._ssl.shutdown(); |
|
|
self._ssl.shutdown(); |
|
@ -95,24 +96,24 @@ function SecurePair(credentials, isServer) { |
|
|
self._destroy(err); |
|
|
self._destroy(err); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.encrypted.write = function(data) { |
|
|
this.encrypted.write = function (data) { |
|
|
debug('encIn data'); |
|
|
debug('encIn data'); |
|
|
self._encInPending.push(data); |
|
|
self._encInPending.push(data); |
|
|
self._cycle(); |
|
|
self._cycle(); |
|
|
return self._encryptedWriteState; |
|
|
return self._encryptedWriteState; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.encrypted.pause = function() { |
|
|
this.encrypted.pause = function () { |
|
|
debug('pause encrypted'); |
|
|
debug('pause encrypted'); |
|
|
self._encryptedWriteState = false; |
|
|
self._encryptedWriteState = false; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.encrypted.resume = function() { |
|
|
this.encrypted.resume = function () { |
|
|
debug('resume encrypted'); |
|
|
debug('resume encrypted'); |
|
|
self._encryptedWriteState = true; |
|
|
self._encryptedWriteState = true; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.encrypted.end = function(err) { |
|
|
this.encrypted.end = function (err) { |
|
|
debug('encrypted end'); |
|
|
debug('encrypted end'); |
|
|
if (!self._done) { |
|
|
if (!self._done) { |
|
|
self._ssl.shutdown(); |
|
|
self._ssl.shutdown(); |
|
@ -121,7 +122,7 @@ function SecurePair(credentials, isServer) { |
|
|
self._destroy(err); |
|
|
self._destroy(err); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
this.cleartext.on('end', function(err) { |
|
|
this.cleartext.on('end', function (err) { |
|
|
debug('clearIn end'); |
|
|
debug('clearIn end'); |
|
|
if (!self._done) { |
|
|
if (!self._done) { |
|
|
self._ssl.shutdown(); |
|
|
self._ssl.shutdown(); |
|
@ -130,37 +131,37 @@ function SecurePair(credentials, isServer) { |
|
|
self._destroy(err); |
|
|
self._destroy(err); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.cleartext.on('close', function() { |
|
|
this.cleartext.on('close', function () { |
|
|
debug('source close'); |
|
|
debug('source close'); |
|
|
self.emit('close'); |
|
|
self.emit('close'); |
|
|
self._destroy(); |
|
|
self._destroy(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.encrypted.on('end', function() { |
|
|
this.encrypted.on('end', function () { |
|
|
if (!self._done) { |
|
|
if (!self._done) { |
|
|
self._error(new Error('Encrypted stream ended before secure pair was done')); |
|
|
self._error(new Error('Encrypted stream ended before secure pair was done')); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.encrypted.on('close', function() { |
|
|
this.encrypted.on('close', function () { |
|
|
if (!self._done) { |
|
|
if (!self._done) { |
|
|
self._error(new Error('Encrypted stream closed before secure pair was done')); |
|
|
self._error(new Error('Encrypted stream closed before secure pair was done')); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.cleartext.on('drain', function() { |
|
|
this.cleartext.on('drain', function () { |
|
|
debug('source drain'); |
|
|
debug('source drain'); |
|
|
self._cycle(); |
|
|
self._cycle(); |
|
|
self.encrypted.resume(); |
|
|
self.encrypted.resume(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
this.encrypted.on('drain', function() { |
|
|
this.encrypted.on('drain', function () { |
|
|
debug('target drain'); |
|
|
debug('target drain'); |
|
|
self._cycle(); |
|
|
self._cycle(); |
|
|
self.cleartext.resume(); |
|
|
self.cleartext.resume(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
process.nextTick(function() { |
|
|
process.nextTick(function () { |
|
|
self._ssl.start(); |
|
|
self._ssl.start(); |
|
|
self._cycle(); |
|
|
self._cycle(); |
|
|
}); |
|
|
}); |
|
@ -168,7 +169,8 @@ function SecurePair(credentials, isServer) { |
|
|
|
|
|
|
|
|
util.inherits(SecurePair, events.EventEmitter); |
|
|
util.inherits(SecurePair, events.EventEmitter); |
|
|
|
|
|
|
|
|
exports.createSecurePair = function(credentials, isServer) { |
|
|
|
|
|
|
|
|
exports.createSecurePair = function (credentials, isServer) { |
|
|
var pair = new SecurePair(credentials, isServer); |
|
|
var pair = new SecurePair(credentials, isServer); |
|
|
return pair; |
|
|
return pair; |
|
|
}; |
|
|
}; |
|
@ -183,15 +185,15 @@ exports.createSecurePair = function(credentials, isServer) { |
|
|
* the ciphers. |
|
|
* the ciphers. |
|
|
* |
|
|
* |
|
|
* The four pipelines, using terminology of the client (server is just reversed): |
|
|
* The four pipelines, using terminology of the client (server is just reversed): |
|
|
* 1) Encrypted Output stream (Writing encrypted data to peer) |
|
|
* (1) Encrypted Output stream (Writing encrypted data to peer) |
|
|
* 2) Encrypted Input stream (Reading encrypted data from peer) |
|
|
* (2) Encrypted Input stream (Reading encrypted data from peer) |
|
|
* 3) Cleartext Output stream (Decrypted content from the peer) |
|
|
* (3) Cleartext Output stream (Decrypted content from the peer) |
|
|
* 4) Cleartext Input stream (Cleartext content to send to the peer) |
|
|
* (4) Cleartext Input stream (Cleartext content to send to the peer) |
|
|
* |
|
|
* |
|
|
* This function attempts to pull any available data out of the Cleartext |
|
|
* This function attempts to pull any available data out of the Cleartext |
|
|
* input stream (#4), and the Encrypted input stream (#2). Then it pushes |
|
|
* input stream (4), and the Encrypted input stream (2). Then it pushes |
|
|
* any data available from the cleartext output stream (#3), and finally |
|
|
* any data available from the cleartext output stream (3), and finally |
|
|
* from the Encrypted output stream (#1) |
|
|
* from the Encrypted output stream (1) |
|
|
* |
|
|
* |
|
|
* It is called whenever we do something with OpenSSL -- post reciving content, |
|
|
* It is called whenever we do something with OpenSSL -- post reciving content, |
|
|
* trying to flush, trying to change ciphers, or shutting down the connection. |
|
|
* trying to flush, trying to change ciphers, or shutting down the connection. |
|
@ -199,7 +201,7 @@ exports.createSecurePair = function(credentials, isServer) { |
|
|
* Because it is also called everywhere, we also check if the connection |
|
|
* Because it is also called everywhere, we also check if the connection |
|
|
* has completed negotiation and emit 'secure' from here if it has. |
|
|
* has completed negotiation and emit 'secure' from here if it has. |
|
|
*/ |
|
|
*/ |
|
|
SecurePair.prototype._cycle = function() { |
|
|
SecurePair.prototype._cycle = function () { |
|
|
if (this._done) { |
|
|
if (this._done) { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
@ -279,26 +281,26 @@ SecurePair.prototype._cycle = function() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
mover( |
|
|
mover( |
|
|
function(pool, offset, length) { |
|
|
function (pool, offset, length) { |
|
|
debug('reading from clearOut'); |
|
|
debug('reading from clearOut'); |
|
|
return self._ssl.clearOut(pool, offset, length); |
|
|
return self._ssl.clearOut(pool, offset, length); |
|
|
}, |
|
|
}, |
|
|
function(chunk) { |
|
|
function (chunk) { |
|
|
self.cleartext.emit('data', chunk); |
|
|
self.cleartext.emit('data', chunk); |
|
|
}, |
|
|
}, |
|
|
function(bytesRead) { |
|
|
function (bytesRead) { |
|
|
return bytesRead > 0 && self._cleartextWriteState === true; |
|
|
return bytesRead > 0 && self._cleartextWriteState === true; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
mover( |
|
|
mover( |
|
|
function(pool, offset, length) { |
|
|
function (pool, offset, length) { |
|
|
debug('reading from encOut'); |
|
|
debug('reading from encOut'); |
|
|
return self._ssl.encOut(pool, offset, length); |
|
|
return self._ssl.encOut(pool, offset, length); |
|
|
}, |
|
|
}, |
|
|
function(chunk) { |
|
|
function (chunk) { |
|
|
self.encrypted.emit('data', chunk); |
|
|
self.encrypted.emit('data', chunk); |
|
|
}, |
|
|
}, |
|
|
function(bytesRead) { |
|
|
function (bytesRead) { |
|
|
return bytesRead > 0 && self._encryptedWriteState === true; |
|
|
return bytesRead > 0 && self._encryptedWriteState === true; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
@ -310,8 +312,8 @@ SecurePair.prototype._cycle = function() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
SecurePair.prototype._destroy = function(err) |
|
|
|
|
|
{ |
|
|
SecurePair.prototype._destroy = function (err) { |
|
|
if (!this._done) { |
|
|
if (!this._done) { |
|
|
this._done = true; |
|
|
this._done = true; |
|
|
this._ssl.close(); |
|
|
this._ssl.close(); |
|
@ -320,17 +322,17 @@ SecurePair.prototype._destroy = function(err) |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
SecurePair.prototype._error = function (err) |
|
|
|
|
|
{ |
|
|
SecurePair.prototype._error = function (err) { |
|
|
this.emit('error', err); |
|
|
this.emit('error', err); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
SecurePair.prototype.getPeerCertificate = function (err) |
|
|
|
|
|
{ |
|
|
SecurePair.prototype.getPeerCertificate = function (err) { |
|
|
return this._ssl.getPeerCertificate(); |
|
|
return this._ssl.getPeerCertificate(); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
SecurePair.prototype.getCipher = function (err) |
|
|
|
|
|
{ |
|
|
SecurePair.prototype.getCipher = function (err) { |
|
|
return this._ssl.getCurrentCipher(); |
|
|
return this._ssl.getCurrentCipher(); |
|
|
}; |
|
|
}; |
|
|