Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tls.connect() emitting twice an error event #1119

Closed
user447463 opened this issue Mar 10, 2015 · 11 comments
Closed

tls.connect() emitting twice an error event #1119

user447463 opened this issue Mar 10, 2015 · 11 comments
Labels
confirmed-bug Issues with confirmed bugs. tls Issues and PRs related to the tls subsystem.

Comments

@user447463
Copy link

With certain hosts (and the particular cipher settings used below) tls.connect() appears to emit an error event twice.

var host='85.158.224.100';

var options = { rejectUnauthorized: false,
    servername: host,
    ciphers: 'EXPORT' };

require('tls').connect(443, host, options, function() {
    console.log('success');
}).on('error', function(error) {
    console.log(error);
    console.log();
});

Actual Result

{ [Error: socket hang up] code: 'ECONNRESET' }

[Error: 101057795:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handsh
ake failure:openssl\ssl\s3_pkt.c:1293:SSL alert number 40
101057795:error:140780E5:SSL routines:SSL23_READ:ssl handshake failure:openssl\s
sl\s23_lib.c:138:
]

Expected Result

[Error: 101057795:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handsh
ake failure:openssl\ssl\s3_pkt.c:1293:SSL alert number 40
101057795:error:140780E5:SSL routines:SSL23_READ:ssl handshake failure:openssl\s
sl\s23_lib.c:138:
]

Quick assumption would be it has something to do with SNI, as it does not occur if servername is not set.

@Fishrock123 Fishrock123 added the tls Issues and PRs related to the tls subsystem. label Mar 10, 2015
@mscdex mscdex added the confirmed-bug Issues with confirmed bugs. label Mar 10, 2015
@user447463
Copy link
Author

Update: It is not necessarily SNI related, as it also happens without servername being given.

var host='204.11.109.195';

var options = { rejectUnauthorized: false,
    ciphers: 'EXPORT' };

require('tls').connect(443, host, options, function() {
    console.log('success');
}).on('error', function(error) {
    console.log(error);
    console.log();
});

@skenqbx
Copy link
Contributor

skenqbx commented Mar 11, 2015

The socket hangup is only emitted when socket._hadError === false and the ssl error is only emitted when socket._writableState.errorEmitted === false. Therefore both get emitted.

The following patch lets onHangUp() check socket._writableState.errorEmitted in the next tick to ensure the ssl error is emitted, since the socket hangup does have less useful information.

diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index fcc216b..5f93270 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -947,10 +947,17 @@ exports.connect = function(/* [port, host], options, cb */) {
     // NOTE: This logic is shared with _http_client.js
     if (!socket._hadError) {
       socket._hadError = true;
-      var error = new Error('socket hang up');
-      error.code = 'ECONNRESET';
-      socket.destroy();
-      socket.emit('error', error);
+
+      setImmediate(function() {
+        if (socket._writableState.errorEmitted)
+          return;
+        socket._writableState.errorEmitted = true;
+
+        var error = new Error('socket hang up');
+        error.code = 'ECONNRESET';
+        socket.destroy();
+        socket.emit('error', error);
+      });
     }
   }
   socket.once('end', onHangUp);

Another issue arose while checking on('close', function(hadError) {}); hadError is always false. Not sure why yet.

@Fishrock123
Copy link
Contributor

cc @indutny?

@indutny
Copy link
Member

indutny commented May 15, 2015

Looking.

@indutny
Copy link
Member

indutny commented May 15, 2015

@skenqbx this change looks good to me. May I ask you to send a PR for it? I'll send a PR for fixing the 'close' event's argument in TLS.

Many thanks!

indutny added a commit to indutny/io.js that referenced this issue May 15, 2015
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: nodejs#1119
@indutny
Copy link
Member

indutny commented May 15, 2015

@skenqbx here it is #1711

@skenqbx
Copy link
Contributor

skenqbx commented May 18, 2015

Will do, give me until end of the week.

indutny added a commit that referenced this issue May 22, 2015
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 <[email protected]>
@indutny
Copy link
Member

indutny commented May 22, 2015

Errors are now properly reported as of 80342f6

@silverwind
Copy link
Contributor

sorry, didn't see there's still a task pending here at first.

@skenqbx
Copy link
Contributor

skenqbx commented May 22, 2015

The double emission of errors seems to be handled by #1711.

indutny pushed a commit that referenced this issue May 22, 2015
This fixes a race condition introduced in 80342f6.
`socket.destroy(err)` only emits the passed error when
`socket._writableState.errorEmitted === false`, `ssl.onerror`
sets `errorEmitted = true` just before calling
`socket.destroy()`.

See: #1119
See: #1711
PR-URL: #1769
Reviewed-By: Fedor Indutny <[email protected]>
@skenqbx
Copy link
Contributor

skenqbx commented May 23, 2015

@silverwind This can be closed now, thank you.

andrewdeandrade pushed a commit to andrewdeandrade/node that referenced this issue Jun 3, 2015
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: nodejs/node#1119
PR-URL: nodejs/node#1711
Reviewed-By: Chris Dickinson <[email protected]>
andrewdeandrade pushed a commit to andrewdeandrade/node that referenced this issue Jun 3, 2015
This fixes a race condition introduced in 80342f6.
`socket.destroy(err)` only emits the passed error when
`socket._writableState.errorEmitted === false`, `ssl.onerror`
sets `errorEmitted = true` just before calling
`socket.destroy()`.

See: nodejs/node#1119
See: nodejs/node#1711
PR-URL: nodejs/node#1769
Reviewed-By: Fedor Indutny <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. tls Issues and PRs related to the tls subsystem.
Projects
None yet
Development

No branches or pull requests

6 participants