Browse Source

TLS: simplify logic

v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
55bff5bab9
  1. 175
      lib/tls.js

175
lib/tls.js

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

Loading…
Cancel
Save