|
|
@ -51,7 +51,7 @@ function CryptoStream(pair) { |
|
|
|
|
|
|
|
this.readable = this.writable = true; |
|
|
|
|
|
|
|
this._writeState = true; |
|
|
|
this._paused = false; |
|
|
|
this._pending = []; |
|
|
|
this._pendingCallbacks = []; |
|
|
|
this._pendingBytes = 0; |
|
|
@ -90,11 +90,10 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) { |
|
|
|
|
|
|
|
this._pending.push(data); |
|
|
|
this._pendingCallbacks.push(cb); |
|
|
|
|
|
|
|
this._pendingBytes += data.length; |
|
|
|
|
|
|
|
this.pair._writeCalled = true; |
|
|
|
this.pair._cycle(); |
|
|
|
this.pair.cycle(); |
|
|
|
|
|
|
|
return this._pendingBytes < 128 * 1024; |
|
|
|
}; |
|
|
@ -102,14 +101,14 @@ CryptoStream.prototype.write = function(data /* , encoding, cb */) { |
|
|
|
|
|
|
|
CryptoStream.prototype.pause = function() { |
|
|
|
debug('paused ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted')); |
|
|
|
this._writeState = false; |
|
|
|
this._paused = true; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
CryptoStream.prototype.resume = function() { |
|
|
|
debug('resume ' + (this == this.pair.cleartext ? 'cleartext' : 'encrypted')); |
|
|
|
this._writeState = true; |
|
|
|
this.pair._cycle(); |
|
|
|
this._paused = false; |
|
|
|
this.pair.cycle(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -147,8 +146,8 @@ function parseCertString(s) { |
|
|
|
|
|
|
|
|
|
|
|
CryptoStream.prototype.getPeerCertificate = function() { |
|
|
|
if (this.pair._ssl) { |
|
|
|
var c = this.pair._ssl.getPeerCertificate(); |
|
|
|
if (this.pair.ssl) { |
|
|
|
var c = this.pair.ssl.getPeerCertificate(); |
|
|
|
|
|
|
|
if (c) { |
|
|
|
if (c.issuer) c.issuer = parseCertString(c.issuer); |
|
|
@ -162,8 +161,8 @@ CryptoStream.prototype.getPeerCertificate = function() { |
|
|
|
|
|
|
|
|
|
|
|
CryptoStream.prototype.getCipher = function(err) { |
|
|
|
if (this.pair._ssl) { |
|
|
|
return this.pair._ssl.getCurrentCipher(); |
|
|
|
if (this.pair.ssl) { |
|
|
|
return this.pair.ssl.getCurrentCipher(); |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
} |
|
|
@ -171,23 +170,22 @@ CryptoStream.prototype.getCipher = function(err) { |
|
|
|
|
|
|
|
|
|
|
|
CryptoStream.prototype.end = function(d) { |
|
|
|
if (this.writable) { |
|
|
|
if (this.pair._done) return; |
|
|
|
if (this.pair._doneFlag) return; |
|
|
|
if (!this.writable) return; |
|
|
|
|
|
|
|
if (d) { |
|
|
|
this.write(d); |
|
|
|
} |
|
|
|
if (d) { |
|
|
|
this.write(d); |
|
|
|
} |
|
|
|
|
|
|
|
this._pending.push(END_OF_FILE); |
|
|
|
this._pendingCallbacks.push(null); |
|
|
|
this._pending.push(END_OF_FILE); |
|
|
|
this._pendingCallbacks.push(null); |
|
|
|
|
|
|
|
// If this is an encrypted stream then we need to disable further 'data'
|
|
|
|
// events.
|
|
|
|
// If this is an encrypted stream then we need to disable further 'data'
|
|
|
|
// events.
|
|
|
|
|
|
|
|
this.writable = false; |
|
|
|
this.writable = false; |
|
|
|
|
|
|
|
this.pair._cycle(); |
|
|
|
} |
|
|
|
this.pair.cycle(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -201,8 +199,8 @@ CryptoStream.prototype.destroySoon = function(err) { |
|
|
|
|
|
|
|
|
|
|
|
CryptoStream.prototype.destroy = function(err) { |
|
|
|
if (this.pair._done) return; |
|
|
|
this.pair._destroy(); |
|
|
|
if (this.pair._doneFlag) return; |
|
|
|
this.pair.destroy(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -211,9 +209,9 @@ CryptoStream.prototype._done = function() { |
|
|
|
|
|
|
|
if (this.pair.cleartext._doneFlag && |
|
|
|
this.pair.encrypted._doneFlag && |
|
|
|
!this.pair._done) { |
|
|
|
!this.pair._doneFlag) { |
|
|
|
// If both streams are done:
|
|
|
|
this.pair._destroy(); |
|
|
|
this.pair.destroy(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -241,7 +239,7 @@ CryptoStream.prototype._push = function() { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
while (this._writeState == true) { |
|
|
|
while (!this._paused) { |
|
|
|
var bytesRead = 0; |
|
|
|
var chunkBytes = 0; |
|
|
|
var pool = new Buffer(16 * 4096); // alloc every time?
|
|
|
@ -249,18 +247,18 @@ CryptoStream.prototype._push = function() { |
|
|
|
do { |
|
|
|
chunkBytes = this._pusher(pool, bytesRead, pool.length - bytesRead); |
|
|
|
|
|
|
|
if (this.pair._ssl && this.pair._ssl.error) { |
|
|
|
this.pair._error(); |
|
|
|
if (this.pair.ssl && this.pair.ssl.error) { |
|
|
|
this.pair.error(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this.pair._maybeInitFinished(); |
|
|
|
this.pair.maybeInitFinished(); |
|
|
|
|
|
|
|
if (chunkBytes >= 0) { |
|
|
|
bytesRead += chunkBytes; |
|
|
|
} |
|
|
|
|
|
|
|
} while ((chunkBytes > 0) && (bytesRead < pool.length)); |
|
|
|
} while (chunkBytes > 0 && bytesRead < pool.length); |
|
|
|
|
|
|
|
assert(bytesRead >= 0); |
|
|
|
|
|
|
@ -313,7 +311,7 @@ CryptoStream.prototype._pull = function() { |
|
|
|
assert(havePending || this._pendingBytes == 0); |
|
|
|
|
|
|
|
while (this._pending.length > 0) { |
|
|
|
if (!this.pair._ssl) break; |
|
|
|
if (!this.pair.ssl) break; |
|
|
|
|
|
|
|
var tmp = this._pending.shift(); |
|
|
|
var cb = this._pendingCallbacks.shift(); |
|
|
@ -330,7 +328,7 @@ CryptoStream.prototype._pull = function() { |
|
|
|
assert(this === this.pair.cleartext); |
|
|
|
debug('end cleartext'); |
|
|
|
|
|
|
|
this.pair._ssl.shutdown(); |
|
|
|
this.pair.ssl.shutdown(); |
|
|
|
|
|
|
|
// TODO check if we get EAGAIN From shutdown, would have to do it
|
|
|
|
// again. should unshift END_OF_FILE back onto pending and wait for
|
|
|
@ -338,7 +336,7 @@ CryptoStream.prototype._pull = function() { |
|
|
|
|
|
|
|
this.pair.encrypted._destroyAfterPush = true; |
|
|
|
} |
|
|
|
this.pair._cycle(); |
|
|
|
this.pair.cycle(); |
|
|
|
this._done() |
|
|
|
return; |
|
|
|
} |
|
|
@ -347,12 +345,12 @@ CryptoStream.prototype._pull = function() { |
|
|
|
|
|
|
|
var rv = this._puller(tmp); |
|
|
|
|
|
|
|
if (this.pair._ssl && this.pair._ssl.error) { |
|
|
|
this.pair._error(); |
|
|
|
if (this.pair.ssl && this.pair.ssl.error) { |
|
|
|
this.pair.error(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this.pair._maybeInitFinished(); |
|
|
|
this.pair.maybeInitFinished(); |
|
|
|
|
|
|
|
if (rv === 0 || rv < 0) { |
|
|
|
this._pending.unshift(tmp); |
|
|
@ -384,8 +382,8 @@ util.inherits(CleartextStream, CryptoStream); |
|
|
|
|
|
|
|
|
|
|
|
CleartextStream.prototype._internallyPendingBytes = function() { |
|
|
|
if (this.pair._ssl) { |
|
|
|
return this.pair._ssl.clearPending(); |
|
|
|
if (this.pair.ssl) { |
|
|
|
return this.pair.ssl.clearPending(); |
|
|
|
} else { |
|
|
|
return 0; |
|
|
|
} |
|
|
@ -394,14 +392,14 @@ CleartextStream.prototype._internallyPendingBytes = function() { |
|
|
|
|
|
|
|
CleartextStream.prototype._puller = function(b) { |
|
|
|
debug('clearIn ' + b.length + ' bytes'); |
|
|
|
return this.pair._ssl.clearIn(b, 0, b.length); |
|
|
|
return this.pair.ssl.clearIn(b, 0, b.length); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
CleartextStream.prototype._pusher = function(pool, offset, length) { |
|
|
|
debug('reading from clearOut'); |
|
|
|
if (!this.pair._ssl) return -1; |
|
|
|
return this.pair._ssl.clearOut(pool, offset, length); |
|
|
|
if (!this.pair.ssl) return -1; |
|
|
|
return this.pair.ssl.clearOut(pool, offset, length); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -412,8 +410,8 @@ util.inherits(EncryptedStream, CryptoStream); |
|
|
|
|
|
|
|
|
|
|
|
EncryptedStream.prototype._internallyPendingBytes = function() { |
|
|
|
if (this.pair._ssl) { |
|
|
|
return this.pair._ssl.encPending(); |
|
|
|
if (this.pair.ssl) { |
|
|
|
return this.pair.ssl.encPending(); |
|
|
|
} else { |
|
|
|
return 0; |
|
|
|
} |
|
|
@ -422,14 +420,14 @@ EncryptedStream.prototype._internallyPendingBytes = function() { |
|
|
|
|
|
|
|
EncryptedStream.prototype._puller = function(b) { |
|
|
|
debug('writing from encIn'); |
|
|
|
return this.pair._ssl.encIn(b, 0, b.length); |
|
|
|
return this.pair.ssl.encIn(b, 0, b.length); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
EncryptedStream.prototype._pusher = function(pool, offset, length) { |
|
|
|
debug('reading from encOut'); |
|
|
|
if (!this.pair._ssl) return -1; |
|
|
|
return this.pair._ssl.encOut(pool, offset, length); |
|
|
|
if (!this.pair.ssl) return -1; |
|
|
|
return this.pair.ssl.encOut(pool, offset, length); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -453,9 +451,7 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { |
|
|
|
this._isServer = isServer ? true : false; |
|
|
|
this._encWriteState = true; |
|
|
|
this._clearWriteState = true; |
|
|
|
this._done = false; |
|
|
|
|
|
|
|
var crypto = require('crypto'); |
|
|
|
this._doneFlag = false; |
|
|
|
|
|
|
|
if (!credentials) { |
|
|
|
this.credentials = crypto.createCredentials(); |
|
|
@ -473,7 +469,7 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { |
|
|
|
this._rejectUnauthorized = rejectUnauthorized ? true : false; |
|
|
|
this._requestCert = requestCert ? true : false; |
|
|
|
|
|
|
|
this._ssl = new Connection(this.credentials.context, |
|
|
|
this.ssl = new Connection(this.credentials.context, |
|
|
|
this._isServer ? true : false, |
|
|
|
this._requestCert, |
|
|
|
this._rejectUnauthorized); |
|
|
@ -486,8 +482,8 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) { |
|
|
|
this.encrypted = new EncryptedStream(this); |
|
|
|
|
|
|
|
process.nextTick(function() { |
|
|
|
self._ssl.start(); |
|
|
|
self._cycle(); |
|
|
|
self.ssl.start(); |
|
|
|
self.cycle(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
@ -535,59 +531,54 @@ exports.createSecurePair = function(credentials, |
|
|
|
* Because it is also called everywhere, we also check if the connection has |
|
|
|
* completed negotiation and emit 'secure' from here if it has. |
|
|
|
*/ |
|
|
|
SecurePair.prototype._cycle = function(depth) { |
|
|
|
SecurePair.prototype.cycle = function(depth) { |
|
|
|
if (this._doneFlag) return; |
|
|
|
|
|
|
|
depth = depth ? depth : 0; |
|
|
|
if (this._done) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if(depth == 0) this._writeCalled = false; |
|
|
|
if (depth == 0) this._writeCalled = false; |
|
|
|
|
|
|
|
var established = this._secureEstablished; |
|
|
|
|
|
|
|
if (!this._cycleEncryptedPullLock) { |
|
|
|
this._cycleEncryptedPullLock = true; |
|
|
|
if (!this.cycleEncryptedPullLock) { |
|
|
|
this.cycleEncryptedPullLock = true; |
|
|
|
debug("encrypted._pull"); |
|
|
|
this.encrypted._pull(); |
|
|
|
this._cycleEncryptedPullLock = false; |
|
|
|
this.cycleEncryptedPullLock = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this._cycleCleartextPullLock) { |
|
|
|
this._cycleCleartextPullLock = true; |
|
|
|
if (!this.cycleCleartextPullLock) { |
|
|
|
this.cycleCleartextPullLock = true; |
|
|
|
debug("cleartext._pull"); |
|
|
|
this.cleartext._pull(); |
|
|
|
this._cycleCleartextPullLock = false; |
|
|
|
this.cycleCleartextPullLock = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this._cycleCleartextPushLock) { |
|
|
|
this._cycleCleartextPushLock = true; |
|
|
|
if (!this.cycleCleartextPushLock) { |
|
|
|
this.cycleCleartextPushLock = true; |
|
|
|
debug("cleartext._push"); |
|
|
|
this.cleartext._push(); |
|
|
|
this._cycleCleartextPushLock = false; |
|
|
|
this.cycleCleartextPushLock = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this._cycleEncryptedPushLock) { |
|
|
|
this._cycleEncryptedPushLock = true; |
|
|
|
if (!this.cycleEncryptedPushLock) { |
|
|
|
this.cycleEncryptedPushLock = true; |
|
|
|
debug("encrypted._push"); |
|
|
|
this.encrypted._push(); |
|
|
|
this._cycleEncryptedPushLock = false; |
|
|
|
} |
|
|
|
|
|
|
|
if (this._done) { |
|
|
|
return; |
|
|
|
this.cycleEncryptedPushLock = false; |
|
|
|
} |
|
|
|
|
|
|
|
if ((!established && this._secureEstablished) || |
|
|
|
(depth == 0 && this._writeCalled)) { |
|
|
|
// If we were not established but now we are, let's cycle again.
|
|
|
|
// Or if there is some data to write...
|
|
|
|
this._cycle(depth + 1); |
|
|
|
this.cycle(depth + 1); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
SecurePair.prototype._maybeInitFinished = function() { |
|
|
|
if (this._ssl && !this._secureEstablished && this._ssl.isInitFinished()) { |
|
|
|
SecurePair.prototype.maybeInitFinished = function() { |
|
|
|
if (this.ssl && !this._secureEstablished && this.ssl.isInitFinished()) { |
|
|
|
this._secureEstablished = true; |
|
|
|
debug('secure established'); |
|
|
|
this.emit('secure'); |
|
|
@ -595,14 +586,14 @@ SecurePair.prototype._maybeInitFinished = function() { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
SecurePair.prototype._destroy = function() { |
|
|
|
SecurePair.prototype.destroy = function() { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (!this._done) { |
|
|
|
this._done = true; |
|
|
|
this._ssl.error = null; |
|
|
|
this._ssl.close(); |
|
|
|
this._ssl = null; |
|
|
|
if (!this._doneFlag) { |
|
|
|
this._doneFlag = true; |
|
|
|
this.ssl.error = null; |
|
|
|
this.ssl.close(); |
|
|
|
this.ssl = null; |
|
|
|
|
|
|
|
self.encrypted.writable = self.encrypted.readable = false; |
|
|
|
self.cleartext.writable = self.cleartext.readable = false; |
|
|
@ -612,23 +603,21 @@ SecurePair.prototype._destroy = function() { |
|
|
|
self.cleartext.emit('close'); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
this._cycle(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
SecurePair.prototype._error = function() { |
|
|
|
SecurePair.prototype.error = function() { |
|
|
|
if (!this._secureEstablished) { |
|
|
|
this._destroy(); |
|
|
|
this.destroy(); |
|
|
|
} else { |
|
|
|
var err = this._ssl.error; |
|
|
|
this._ssl.error = null; |
|
|
|
var err = this.ssl.error; |
|
|
|
this.ssl.error = null; |
|
|
|
|
|
|
|
if (this._isServer && |
|
|
|
this._rejectUnauthorized && |
|
|
|
/peer did not return a certificate/.test(err.message)) { |
|
|
|
// Not really an error.
|
|
|
|
this._destroy(); |
|
|
|
this.destroy(); |
|
|
|
} else { |
|
|
|
this.cleartext.emit('error', err); |
|
|
|
} |
|
|
@ -749,13 +738,13 @@ function Server(/* [options], listener */) { |
|
|
|
cleartext._controlReleased = true; |
|
|
|
self.emit('secureConnection', pair.cleartext, pair.encrypted); |
|
|
|
} else { |
|
|
|
var verifyError = pair._ssl.verifyError(); |
|
|
|
var verifyError = pair.ssl.verifyError(); |
|
|
|
if (verifyError) { |
|
|
|
pair.cleartext.authorizationError = verifyError; |
|
|
|
|
|
|
|
if (self.rejectUnauthorized) { |
|
|
|
socket.destroy(); |
|
|
|
pair._destroy(); |
|
|
|
pair.destroy(); |
|
|
|
} else { |
|
|
|
cleartext._controlReleased = true; |
|
|
|
self.emit('secureConnection', pair.cleartext, pair.encrypted); |
|
|
@ -851,7 +840,7 @@ exports.connect = function(port /* host, options, cb */) { |
|
|
|
socket.connect(port, host); |
|
|
|
|
|
|
|
pair.on('secure', function() { |
|
|
|
var verifyError = pair._ssl.verifyError(); |
|
|
|
var verifyError = pair.ssl.verifyError(); |
|
|
|
|
|
|
|
if (verifyError) { |
|
|
|
cleartext.authorized = false; |
|
|
|