Skip to content

Commit

Permalink
tls: use .destroy(err) instead of destroy+emit
Browse files Browse the repository at this point in the history
Emit errors using `.destroy(err)` instead of `.destroy()` and
`.emit('error', err)`. Otherwise `close` event is emitted with the
`error` argument set to `false`, even if the connection was torn down
because of the error.

See: #1119
PR-URL: #1711
Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
  • Loading branch information
indutny committed May 22, 2015
1 parent 2b1c01c commit 80342f6
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
36 changes: 22 additions & 14 deletions lib/_tls_wrap.js
Expand Up @@ -38,7 +38,7 @@ function onhandshakestart() {
// callback to destroy the connection right now, it would crash and burn.
setImmediate(function() {
var err = new Error('TLS session renegotiation attack detected.');
self._tlsError(err);
self._emitTLSError(err);
});
}
}
Expand Down Expand Up @@ -233,7 +233,7 @@ function TLSSocket(socket, options) {
// Proxy for API compatibility
this.ssl = this._handle;

this.on('error', this._tlsError);
this.on('error', this._emitTLSError);

this._init(socket, wrap);

Expand Down Expand Up @@ -363,16 +363,15 @@ TLSSocket.prototype._init = function(socket, wrap) {

// Destroy socket if error happened before handshake's finish
if (!self._secureEstablished) {
self._tlsError(err);
self.destroy();
self.destroy(self._tlsError(err));
} else if (options.isServer &&
rejectUnauthorized &&
/peer did not return a certificate/.test(err.message)) {
// Ignore server's authorization errors
self.destroy();
} else {
// Throw error
self._tlsError(err);
self._emitTLSError(err);
}
};

Expand Down Expand Up @@ -416,7 +415,7 @@ TLSSocket.prototype._init = function(socket, wrap) {
// Assume `tls.connect()`
if (wrap) {
wrap.on('error', function(err) {
self._tlsError(err);
self._emitTLSError(err);
});
} else {
assert(!socket);
Expand Down Expand Up @@ -472,20 +471,27 @@ TLSSocket.prototype.getTLSTicket = function getTLSTicket() {
};

TLSSocket.prototype._handleTimeout = function() {
this._tlsError(new Error('TLS handshake timeout'));
this._emitTLSError(new Error('TLS handshake timeout'));
};

TLSSocket.prototype._emitTLSError = function(err) {
var e = this._tlsError(err);
if (e)
this.emit('error', e);
};

TLSSocket.prototype._tlsError = function(err) {
this.emit('_tlsError', err);
if (this._controlReleased)
this.emit('error', err);
return err;
return null;
};

TLSSocket.prototype._releaseControl = function() {
if (this._controlReleased)
return false;
this._controlReleased = true;
this.removeListener('error', this._tlsError);
this.removeListener('error', this._emitTLSError);
return true;
};

Expand Down Expand Up @@ -717,7 +723,11 @@ function Server(/* [options], listener */) {
});

var errorEmitted = false;
socket.on('close', function() {
socket.on('close', function(err) {
// Closed because of error - no need to emit it twice
if (err)
return;

// Emit ECONNRESET
if (!socket._controlReleased && !errorEmitted) {
errorEmitted = true;
Expand Down Expand Up @@ -936,8 +946,7 @@ exports.connect = function(/* [port, host], options, cb */) {
socket.authorizationError = verifyError.code || verifyError.message;

if (options.rejectUnauthorized) {
socket.emit('error', verifyError);
socket.destroy();
socket.destroy(verifyError);
return;
} else {
socket.emit('secureConnect');
Expand All @@ -957,8 +966,7 @@ exports.connect = function(/* [port, host], options, cb */) {
socket._hadError = true;
var error = new Error('socket hang up');
error.code = 'ECONNRESET';
socket.destroy();
socket.emit('error', error);
socket.destroy(error);
}
}
socket.once('end', onHangUp);
Expand Down
42 changes: 42 additions & 0 deletions test/parallel/test-tls-close-error.js
@@ -0,0 +1,42 @@
'use strict';

var assert = require('assert');
var common = require('../common');

if (!common.hasCrypto) {
console.log('1..0 # Skipped: missing crypto');
process.exit();
}
var tls = require('tls');

var fs = require('fs');
var net = require('net');

var errorCount = 0;
var closeCount = 0;

var server = tls.createServer({
key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
}, function(c) {
}).listen(common.PORT, function() {
var c = tls.connect(common.PORT, function() {
assert(false, 'should not be called');
});

c.on('error', function(err) {
errorCount++;
});

c.on('close', function(err) {
if (err)
closeCount++;

server.close();
});
});

process.on('exit', function() {
assert.equal(errorCount, 1);
assert.equal(closeCount, 1);
});

0 comments on commit 80342f6

Please sign in to comment.