diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 5b86672b959143..4caf97bba334d0 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -774,7 +774,10 @@ TLSSocket.prototype._finishInit = function() { return; this.alpnProtocol = this._handle.getALPNNegotiatedProtocol(); - this.servername = this._handle.getServername(); + // The servername could be set by TLSWrap::SelectSNIContextCallback(). + if (this.servername === null) { + this.servername = this._handle.getServername(); + } debug('%s _finishInit', this._tlsOptions.isServer ? 'server' : 'client', diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index cd6321b9693c3b..a1944df0563731 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -1033,6 +1033,14 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { Local object = p->object(); Local ctx; + // Set the servername as early as possible + Local owner = p->GetOwner(); + if (!owner->Set(env->context(), + env->servername_string(), + OneByteString(env->isolate(), servername)).FromMaybe(false)) { + return SSL_TLSEXT_ERR_NOACK; + } + if (!object->Get(env->context(), env->sni_context_string()).ToLocal(&ctx)) return SSL_TLSEXT_ERR_NOACK; diff --git a/test/parallel/test-tls-sni-servername.js b/test/parallel/test-tls-sni-servername.js new file mode 100644 index 00000000000000..2c5785df5426c9 --- /dev/null +++ b/test/parallel/test-tls-sni-servername.js @@ -0,0 +1,56 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); + +// We could get the `tlsSocket.servername` even if the event of "tlsClientError" +// is emitted. + +const serverOptions = { + requestCert: true, + rejectUnauthorized: false, + SNICallback: function(servername, callback) { + if (servername === 'c.another.com') { + callback(null, {}); + } else { + callback(new Error('Invalid SNI context'), null); + } + } +}; + +function test(options) { + const server = tls.createServer(serverOptions, common.mustNotCall()); + + server.on('tlsClientError', common.mustCall((err, socket) => { + assert.strictEqual(err.message, 'Invalid SNI context'); + // The `servername` should match. + assert.strictEqual(socket.servername, options.servername); + })); + + server.listen(0, () => { + options.port = server.address().port; + const client = tls.connect(options, common.mustNotCall()); + + client.on('error', common.mustCall((err) => { + assert.strictEqual(err.message, 'Client network socket' + + ' disconnected before secure TLS connection was established'); + })); + + client.on('close', common.mustCall(() => server.close())); + }); +} + +test({ + port: undefined, + servername: 'c.another.com', + rejectUnauthorized: false +}); + +test({ + port: undefined, + servername: 'c.wrong.com', + rejectUnauthorized: false +});