1'use strict';
2const common = require('../common');
3const fixtures = require('../common/fixtures');
4
5if (!common.hasCrypto)
6  common.skip('missing crypto');
7
8// This test ensures that when a TLS connection is established, the server
9// selects the most recently added SecureContext that matches the servername.
10
11const assert = require('assert');
12const tls = require('tls');
13
14function loadPEM(n) {
15  return fixtures.readKey(`${n}.pem`);
16}
17
18const serverOptions = {
19  key: loadPEM('agent2-key'),
20  cert: loadPEM('agent2-cert'),
21  requestCert: true,
22  rejectUnauthorized: false,
23};
24
25const badSecureContext = {
26  key: loadPEM('agent1-key'),
27  cert: loadPEM('agent1-cert'),
28  ca: [ loadPEM('ca2-cert') ]
29};
30
31const goodSecureContext = {
32  key: loadPEM('agent1-key'),
33  cert: loadPEM('agent1-cert'),
34  ca: [ loadPEM('ca1-cert') ]
35};
36
37const server = tls.createServer(serverOptions, (c) => {
38  // The 'a' and 'b' subdomains are used to distinguish between client
39  // connections.
40  // Connection to subdomain 'a' is made when the 'bad' secure context is
41  // the only one in use.
42  if ('a.example.com' === c.servername) {
43    assert.strictEqual(c.authorized, false);
44  }
45  // Connection to subdomain 'b' is made after the 'good' context has been
46  // added.
47  if ('b.example.com' === c.servername) {
48    assert.strictEqual(c.authorized, true);
49  }
50});
51
52// 1. Add the 'bad' secure context. A connection using this context will not be
53// authorized.
54server.addContext('*.example.com', badSecureContext);
55
56server.listen(0, () => {
57  const options = {
58    port: server.address().port,
59    key: loadPEM('agent1-key'),
60    cert: loadPEM('agent1-cert'),
61    ca: [loadPEM('ca1-cert')],
62    servername: 'a.example.com',
63    rejectUnauthorized: false,
64  };
65
66  // 2. Make a connection using servername 'a.example.com'. Since a 'bad'
67  // secure context is used, this connection should not be authorized.
68  const client = tls.connect(options, () => {
69    client.end();
70  });
71
72  client.on('close', common.mustCall(() => {
73    // 3. Add a 'good' secure context.
74    server.addContext('*.example.com', goodSecureContext);
75
76    options.servername = 'b.example.com';
77    // 4. Make a connection using servername 'b.example.com'. This connection
78    // should be authorized because the 'good' secure context is the most
79    // recently added matching context.
80
81    const other = tls.connect(options, () => {
82      other.end();
83    });
84
85    other.on('close', common.mustCall(() => {
86      // 5. Make another connection using servername 'b.example.com' to ensure
87      // that the array of secure contexts is not reversed in place with each
88      // SNICallback call, as someone might be tempted to refactor this piece of
89      // code by using Array.prototype.reverse() method.
90      const onemore = tls.connect(options, () => {
91        onemore.end();
92      });
93
94      onemore.on('close', common.mustCall(() => {
95        server.close();
96      }));
97    }));
98  }));
99});
100