2024-01-01 14:58:21 -05:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2024-01-23 11:35:23 -05:00
|
|
|
import { core, internals, primordials } from "ext:core/mod.js";
|
2024-01-26 14:04:07 -05:00
|
|
|
const { internalRidSymbol } = core;
|
2024-01-26 17:46:46 -05:00
|
|
|
import {
|
2023-12-26 20:30:26 -05:00
|
|
|
op_net_accept_tls,
|
|
|
|
op_net_connect_tls,
|
2024-01-10 17:37:25 -05:00
|
|
|
op_net_listen_tls,
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
op_tls_cert_resolver_create,
|
|
|
|
op_tls_cert_resolver_poll,
|
|
|
|
op_tls_cert_resolver_resolve,
|
|
|
|
op_tls_cert_resolver_resolve_error,
|
2024-01-10 17:37:25 -05:00
|
|
|
op_tls_handshake,
|
2024-04-08 17:01:02 -04:00
|
|
|
op_tls_key_null,
|
|
|
|
op_tls_key_static,
|
|
|
|
op_tls_key_static_from_file,
|
2024-01-10 17:37:25 -05:00
|
|
|
op_tls_start,
|
2024-01-26 17:46:46 -05:00
|
|
|
} from "ext:core/ops";
|
2024-01-10 17:37:25 -05:00
|
|
|
const {
|
|
|
|
Number,
|
2024-01-26 17:19:00 -05:00
|
|
|
ObjectDefineProperty,
|
2024-01-10 17:37:25 -05:00
|
|
|
TypeError,
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
SymbolFor,
|
2024-01-10 17:37:25 -05:00
|
|
|
} = primordials;
|
|
|
|
|
|
|
|
import { Conn, Listener } from "ext:deno_net/01_net.js";
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
class TlsConn extends Conn {
|
2024-01-24 16:12:10 -05:00
|
|
|
#rid = 0;
|
|
|
|
|
|
|
|
constructor(rid, remoteAddr, localAddr) {
|
|
|
|
super(rid, remoteAddr, localAddr);
|
2024-01-26 17:19:00 -05:00
|
|
|
ObjectDefineProperty(this, internalRidSymbol, {
|
|
|
|
enumerable: false,
|
|
|
|
value: rid,
|
|
|
|
});
|
2024-01-24 16:12:10 -05:00
|
|
|
this.#rid = rid;
|
|
|
|
}
|
|
|
|
|
|
|
|
get rid() {
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsConn.rid",
|
|
|
|
new Error().stack,
|
|
|
|
"Use `Deno.TlsConn` instance methods instead.",
|
|
|
|
);
|
|
|
|
return this.#rid;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
handshake() {
|
2024-02-13 15:34:36 -05:00
|
|
|
return op_tls_handshake(this.#rid);
|
2021-10-26 16:27:47 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
2021-10-26 16:27:47 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
async function connectTls({
|
|
|
|
port,
|
|
|
|
hostname = "127.0.0.1",
|
|
|
|
transport = "tcp",
|
2024-04-11 16:31:11 -04:00
|
|
|
caCerts = [],
|
2024-04-18 13:21:08 -04:00
|
|
|
alpnProtocols = undefined,
|
|
|
|
keyFormat = undefined,
|
2024-04-11 16:31:11 -04:00
|
|
|
cert = undefined,
|
2024-04-18 13:21:08 -04:00
|
|
|
certFile = undefined,
|
|
|
|
certChain = undefined,
|
2024-04-11 16:31:11 -04:00
|
|
|
key = undefined,
|
2024-04-18 13:21:08 -04:00
|
|
|
keyFile = undefined,
|
|
|
|
privateKey = undefined,
|
2023-02-07 14:22:46 -05:00
|
|
|
}) {
|
2024-04-11 16:31:11 -04:00
|
|
|
if (transport !== "tcp") {
|
|
|
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
|
|
|
}
|
2024-04-18 13:21:08 -04:00
|
|
|
let deprecatedCertFile = undefined;
|
|
|
|
|
|
|
|
// Deno.connectTls has an irregular option where you can just pass `certFile` and
|
|
|
|
// not `keyFile`. In this case it's used for `caCerts` rather than the client key.
|
|
|
|
if (certFile !== undefined && keyFile === undefined) {
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.ConnectTlsOptions.certFile",
|
|
|
|
new Error().stack,
|
|
|
|
"Pass the cert file's contents to the `Deno.ConnectTlsOptions.caCerts` option instead.",
|
2024-04-11 16:31:11 -04:00
|
|
|
);
|
2024-04-18 13:21:08 -04:00
|
|
|
|
|
|
|
deprecatedCertFile = certFile;
|
|
|
|
certFile = undefined;
|
2024-04-11 16:31:11 -04:00
|
|
|
}
|
2024-04-18 13:21:08 -04:00
|
|
|
|
|
|
|
const keyPair = loadTlsKeyPair("Deno.connectTls", {
|
|
|
|
keyFormat,
|
|
|
|
cert,
|
|
|
|
certFile,
|
|
|
|
certChain,
|
|
|
|
key,
|
|
|
|
keyFile,
|
|
|
|
privateKey,
|
|
|
|
});
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
// TODO(mmastrac): We only expose this feature via symbol for now. This should actually be a feature
|
|
|
|
// in Deno.connectTls, however.
|
|
|
|
const serverName = arguments[0][serverNameSymbol] ?? null;
|
2023-12-26 20:30:26 -05:00
|
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_connect_tls(
|
2023-02-07 14:22:46 -05:00
|
|
|
{ hostname, port },
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
{ certFile: deprecatedCertFile, caCerts, alpnProtocols, serverName },
|
2024-04-08 17:01:02 -04:00
|
|
|
keyPair,
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
|
|
|
localAddr.transport = "tcp";
|
|
|
|
remoteAddr.transport = "tcp";
|
|
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
|
|
|
}
|
2021-10-26 16:27:47 -04:00
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
class TlsListener extends Listener {
|
2024-01-24 17:50:33 -05:00
|
|
|
#rid = 0;
|
|
|
|
|
|
|
|
constructor(rid, addr) {
|
|
|
|
super(rid, addr);
|
2024-01-26 17:19:00 -05:00
|
|
|
ObjectDefineProperty(this, internalRidSymbol, {
|
|
|
|
enumerable: false,
|
|
|
|
value: rid,
|
|
|
|
});
|
2024-01-24 17:50:33 -05:00
|
|
|
this.#rid = rid;
|
|
|
|
}
|
|
|
|
|
|
|
|
get rid() {
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsListener.rid",
|
|
|
|
new Error().stack,
|
|
|
|
"Use `Deno.TlsListener` instance methods instead.",
|
|
|
|
);
|
|
|
|
return this.#rid;
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
async accept() {
|
2023-12-26 20:30:26 -05:00
|
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await op_net_accept_tls(
|
2024-01-24 17:50:33 -05:00
|
|
|
this.#rid,
|
2022-10-25 16:50:55 -04:00
|
|
|
);
|
|
|
|
localAddr.transport = "tcp";
|
|
|
|
remoteAddr.transport = "tcp";
|
|
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2023-02-07 14:22:46 -05:00
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2024-04-18 13:21:08 -04:00
|
|
|
/**
|
|
|
|
* Returns true if this object has the shape of one of the certified key material
|
|
|
|
* interfaces.
|
|
|
|
*/
|
2024-04-08 17:01:02 -04:00
|
|
|
function hasTlsKeyPairOptions(options) {
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
// TODO(mmastrac): remove this temporary symbol when the API lands
|
|
|
|
if (options[resolverSymbol] !== undefined) {
|
|
|
|
return true;
|
|
|
|
}
|
2024-04-24 14:27:15 -04:00
|
|
|
return (options.cert !== undefined || options.key !== undefined ||
|
|
|
|
options.certFile !== undefined ||
|
|
|
|
options.keyFile !== undefined || options.privateKey !== undefined ||
|
|
|
|
options.certChain !== undefined);
|
2024-04-08 17:01:02 -04:00
|
|
|
}
|
|
|
|
|
2024-04-18 13:21:08 -04:00
|
|
|
/**
|
|
|
|
* Loads a TLS keypair from one of the various options. If no key material is provided,
|
|
|
|
* returns a special Null keypair.
|
|
|
|
*/
|
|
|
|
function loadTlsKeyPair(api, {
|
|
|
|
keyFormat,
|
2024-04-08 17:01:02 -04:00
|
|
|
cert,
|
|
|
|
certFile,
|
2024-04-18 13:21:08 -04:00
|
|
|
certChain,
|
2024-04-08 17:01:02 -04:00
|
|
|
key,
|
|
|
|
keyFile,
|
2024-04-18 13:21:08 -04:00
|
|
|
privateKey,
|
|
|
|
}) {
|
2024-04-20 21:58:59 -04:00
|
|
|
if (internals.future) {
|
|
|
|
certFile = undefined;
|
|
|
|
certChain = undefined;
|
|
|
|
keyFile = undefined;
|
|
|
|
privateKey = undefined;
|
|
|
|
}
|
|
|
|
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
// TODO(mmastrac): remove this temporary symbol when the API lands
|
|
|
|
if (arguments[1][resolverSymbol] !== undefined) {
|
|
|
|
return createTlsKeyResolver(arguments[1][resolverSymbol]);
|
|
|
|
}
|
|
|
|
|
2024-04-18 13:21:08 -04:00
|
|
|
// Check for "pem" format
|
|
|
|
if (keyFormat !== undefined && keyFormat !== "pem") {
|
|
|
|
throw new TypeError('If `keyFormat` is specified, it must be "pem"');
|
2024-04-08 17:01:02 -04:00
|
|
|
}
|
2024-04-18 13:21:08 -04:00
|
|
|
|
|
|
|
function exclusive(a1, a1v, a2, a2v) {
|
|
|
|
if (a1v !== undefined && a2v !== undefined) {
|
|
|
|
throw new TypeError(
|
|
|
|
`Cannot specify both \`${a1}\` and \`${a2}\` for \`${api}\`.`,
|
|
|
|
);
|
|
|
|
}
|
2024-04-08 17:01:02 -04:00
|
|
|
}
|
|
|
|
|
2024-04-18 13:21:08 -04:00
|
|
|
// Ensure that only one pair is valid
|
|
|
|
exclusive("certChain", certChain, "cert", cert);
|
|
|
|
exclusive("certChain", certChain, "certFile", certFile);
|
|
|
|
exclusive("key", key, "keyFile", keyFile);
|
|
|
|
exclusive("key", key, "privateKey", privateKey);
|
|
|
|
|
|
|
|
function both(a1, a1v, a2, a2v) {
|
|
|
|
if (a1v !== undefined && a2v === undefined) {
|
|
|
|
throw new TypeError(
|
|
|
|
`If \`${a1}\` is specified, \`${a2}\` must be specified as well for \`${api}\`.`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (a1v === undefined && a2v !== undefined) {
|
|
|
|
throw new TypeError(
|
|
|
|
`If \`${a2}\` is specified, \`${a1}\` must be specified as well for \`${api}\`.`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick one pair of cert/key, certFile/keyFile or certChain/privateKey
|
|
|
|
both("cert", cert, "key", key);
|
|
|
|
both("certFile", certFile, "keyFile", keyFile);
|
|
|
|
both("certChain", certChain, "privateKey", privateKey);
|
|
|
|
|
2024-04-08 17:01:02 -04:00
|
|
|
if (certFile !== undefined) {
|
2024-04-18 13:21:08 -04:00
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsCertifiedKeyOptions.keyFile",
|
|
|
|
new Error().stack,
|
|
|
|
"Pass the key file's contents to the `Deno.TlsCertifiedKeyPem.key` option instead.",
|
|
|
|
);
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsCertifiedKeyOptions.certFile",
|
|
|
|
new Error().stack,
|
|
|
|
"Pass the cert file's contents to the `Deno.TlsCertifiedKeyPem.cert` option instead.",
|
|
|
|
);
|
|
|
|
return op_tls_key_static_from_file(api, certFile, keyFile);
|
|
|
|
} else if (certChain !== undefined) {
|
|
|
|
if (api !== "Deno.connectTls") {
|
|
|
|
throw new TypeError(
|
|
|
|
`Invalid options 'certChain' and 'privateKey' for ${api}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsCertifiedKeyOptions.privateKey",
|
|
|
|
new Error().stack,
|
|
|
|
"Use the `Deno.TlsCertifiedKeyPem.key` option instead.",
|
|
|
|
);
|
|
|
|
internals.warnOnDeprecatedApi(
|
|
|
|
"Deno.TlsCertifiedKeyOptions.certChain",
|
|
|
|
new Error().stack,
|
|
|
|
"Use the `Deno.TlsCertifiedKeyPem.cert` option instead.",
|
|
|
|
);
|
|
|
|
return op_tls_key_static(certChain, privateKey);
|
2024-04-08 17:01:02 -04:00
|
|
|
} else if (cert !== undefined) {
|
|
|
|
return op_tls_key_static(cert, key);
|
|
|
|
} else {
|
|
|
|
return op_tls_key_null();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 14:22:46 -05:00
|
|
|
function listenTls({
|
|
|
|
port,
|
|
|
|
hostname = "0.0.0.0",
|
|
|
|
transport = "tcp",
|
|
|
|
alpnProtocols = undefined,
|
|
|
|
reusePort = false,
|
|
|
|
}) {
|
|
|
|
if (transport !== "tcp") {
|
|
|
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
2020-07-19 13:49:44 -04:00
|
|
|
}
|
2024-04-18 13:21:08 -04:00
|
|
|
|
|
|
|
if (!hasTlsKeyPairOptions(arguments[0])) {
|
|
|
|
throw new TypeError(
|
|
|
|
"A key and certificate are required for `Deno.listenTls`",
|
2024-04-11 16:31:11 -04:00
|
|
|
);
|
2024-04-09 18:23:22 -04:00
|
|
|
}
|
2024-04-18 13:21:08 -04:00
|
|
|
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
|
2024-01-10 17:37:25 -05:00
|
|
|
const { 0: rid, 1: localAddr } = op_net_listen_tls(
|
2023-07-25 02:26:18 -04:00
|
|
|
{ hostname, port: Number(port) },
|
2024-04-08 17:01:02 -04:00
|
|
|
{ alpnProtocols, reusePort },
|
|
|
|
keyPair,
|
2023-02-07 14:22:46 -05:00
|
|
|
);
|
|
|
|
return new TlsListener(rid, localAddr);
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
2024-03-11 23:48:00 -04:00
|
|
|
// deno-lint-ignore require-await
|
2023-02-07 14:22:46 -05:00
|
|
|
async function startTls(
|
|
|
|
conn,
|
|
|
|
{
|
|
|
|
hostname = "127.0.0.1",
|
|
|
|
caCerts = [],
|
2021-11-26 13:59:53 -05:00
|
|
|
alpnProtocols = undefined,
|
2023-02-07 14:22:46 -05:00
|
|
|
} = {},
|
|
|
|
) {
|
2024-03-11 23:48:00 -04:00
|
|
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = op_tls_start({
|
2024-01-26 14:04:07 -05:00
|
|
|
rid: conn[internalRidSymbol],
|
2023-02-07 14:22:46 -05:00
|
|
|
hostname,
|
|
|
|
caCerts,
|
|
|
|
alpnProtocols,
|
|
|
|
});
|
|
|
|
return new TlsConn(rid, remoteAddr, localAddr);
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
refactor(ext/tls): Implement required functionality for later SNI support (#23686)
Precursor to #23236
This implements the SNI features, but uses private symbols to avoid
exposing the functionality at this time. Note that to properly test this
feature, we need to add a way for `connectTls` to specify a hostname.
This is something that should be pushed into that API at a later time as
well.
```ts
Deno.test(
{ permissions: { net: true, read: true } },
async function listenResolver() {
let sniRequests = [];
const listener = Deno.listenTls({
hostname: "localhost",
port: 0,
[resolverSymbol]: (sni: string) => {
sniRequests.push(sni);
return {
cert,
key,
};
},
});
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-1",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
{
const conn = await Deno.connectTls({
hostname: "localhost",
[serverNameSymbol]: "server-2",
port: listener.addr.port,
});
const [_handshake, serverConn] = await Promise.all([
conn.handshake(),
listener.accept(),
]);
conn.close();
serverConn.close();
}
assertEquals(sniRequests, ["server-1", "server-2"]);
listener.close();
},
);
```
---------
Signed-off-by: Matt Mastracci <matthew@mastracci.com>
2024-05-09 12:54:47 -04:00
|
|
|
const resolverSymbol = SymbolFor("unstableSniResolver");
|
|
|
|
const serverNameSymbol = SymbolFor("unstableServerName");
|
|
|
|
|
|
|
|
function createTlsKeyResolver(callback) {
|
|
|
|
const { 0: resolver, 1: lookup } = op_tls_cert_resolver_create();
|
|
|
|
(async () => {
|
|
|
|
while (true) {
|
|
|
|
const sni = await op_tls_cert_resolver_poll(lookup);
|
|
|
|
if (typeof sni !== "string") {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const key = await callback(sni);
|
|
|
|
if (!hasTlsKeyPairOptions(key)) {
|
|
|
|
op_tls_cert_resolver_resolve_error(lookup, sni, "Invalid key");
|
|
|
|
} else {
|
|
|
|
const resolved = loadTlsKeyPair("Deno.listenTls", key);
|
|
|
|
op_tls_cert_resolver_resolve(lookup, sni, resolved);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
op_tls_cert_resolver_resolve_error(lookup, sni, e.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
return resolver;
|
|
|
|
}
|
|
|
|
|
|
|
|
internals.resolverSymbol = resolverSymbol;
|
|
|
|
internals.serverNameSymbol = serverNameSymbol;
|
|
|
|
internals.createTlsKeyResolver = createTlsKeyResolver;
|
|
|
|
|
2024-04-08 17:01:02 -04:00
|
|
|
export {
|
|
|
|
connectTls,
|
|
|
|
hasTlsKeyPairOptions,
|
|
|
|
listenTls,
|
|
|
|
loadTlsKeyPair,
|
|
|
|
startTls,
|
|
|
|
TlsConn,
|
|
|
|
TlsListener,
|
|
|
|
};
|