'use strict'; const common = require('../common'); const fixtures = require('../common/fixtures'); if (!common.hasCrypto) common.skip('missing crypto'); // This test ensures that when a TLS connection is established, the server // selects the most recently added SecureContext that matches the servername. const assert = require('assert'); const tls = require('tls'); function loadPEM(n) { return fixtures.readKey(`${n}.pem`); } const serverOptions = { key: loadPEM('agent2-key'), cert: loadPEM('agent2-cert'), requestCert: true, rejectUnauthorized: false, }; const badSecureContext = { key: loadPEM('agent1-key'), cert: loadPEM('agent1-cert'), ca: [ loadPEM('ca2-cert') ] }; const goodSecureContext = { key: loadPEM('agent1-key'), cert: loadPEM('agent1-cert'), ca: [ loadPEM('ca1-cert') ] }; const server = tls.createServer(serverOptions, (c) => { // The 'a' and 'b' subdomains are used to distinguish between client // connections. // Connection to subdomain 'a' is made when the 'bad' secure context is // the only one in use. if ('a.example.com' === c.servername) { assert.strictEqual(c.authorized, false); } // Connection to subdomain 'b' is made after the 'good' context has been // added. if ('b.example.com' === c.servername) { assert.strictEqual(c.authorized, true); } }); // 1. Add the 'bad' secure context. A connection using this context will not be // authorized. server.addContext('*.example.com', badSecureContext); server.listen(0, () => { const options = { port: server.address().port, key: loadPEM('agent1-key'), cert: loadPEM('agent1-cert'), ca: [loadPEM('ca1-cert')], servername: 'a.example.com', rejectUnauthorized: false, }; // 2. Make a connection using servername 'a.example.com'. Since a 'bad' // secure context is used, this connection should not be authorized. const client = tls.connect(options, () => { client.end(); }); client.on('close', common.mustCall(() => { // 3. Add a 'good' secure context. server.addContext('*.example.com', goodSecureContext); options.servername = 'b.example.com'; // 4. Make a connection using servername 'b.example.com'. This connection // should be authorized because the 'good' secure context is the most // recently added matching context. const other = tls.connect(options, () => { other.end(); }); other.on('close', common.mustCall(() => { // 5. Make another connection using servername 'b.example.com' to ensure // that the array of secure contexts is not reversed in place with each // SNICallback call, as someone might be tempted to refactor this piece of // code by using Array.prototype.reverse() method. const onemore = tls.connect(options, () => { onemore.end(); }); onemore.on('close', common.mustCall(() => { server.close(); })); })); })); });