2021-01-11 12:13:41 -05:00
|
|
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
2020-02-29 12:45:47 -05:00
|
|
|
import {
|
|
|
|
assert,
|
|
|
|
assertEquals,
|
2021-01-14 23:32:27 -05:00
|
|
|
assertStrictEquals,
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows,
|
|
|
|
assertThrowsAsync,
|
2020-11-26 11:22:36 -05:00
|
|
|
deferred,
|
2020-03-28 13:03:49 -04:00
|
|
|
unitTest,
|
2020-02-29 12:45:47 -05:00
|
|
|
} from "./test_util.ts";
|
2021-02-02 06:05:46 -05:00
|
|
|
import { BufReader, BufWriter } from "../../../test_util/std/io/bufio.ts";
|
|
|
|
import { TextProtoReader } from "../../../test_util/std/textproto/mod.ts";
|
2019-10-21 14:38:28 -04:00
|
|
|
|
|
|
|
const encoder = new TextEncoder();
|
|
|
|
const decoder = new TextDecoder();
|
2019-09-23 14:40:38 -04:00
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(async function connectTLSNoPerm(): Promise<void> {
|
2020-06-24 18:57:08 -04:00
|
|
|
await assertThrowsAsync(async () => {
|
2020-04-24 17:29:14 -04:00
|
|
|
await Deno.connectTls({ hostname: "github.com", port: 443 });
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Deno.errors.PermissionDenied);
|
2019-09-23 14:40:38 -04:00
|
|
|
});
|
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(async function connectTLSCertFileNoReadPerm(): Promise<void> {
|
2020-06-24 18:57:08 -04:00
|
|
|
await assertThrowsAsync(async () => {
|
2020-04-24 17:29:14 -04:00
|
|
|
await Deno.connectTls({
|
2019-10-21 14:38:28 -04:00
|
|
|
hostname: "github.com",
|
|
|
|
port: 443,
|
2020-03-28 13:03:49 -04:00
|
|
|
certFile: "cli/tests/tls/RootCA.crt",
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Deno.errors.PermissionDenied);
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
2020-03-20 09:38:34 -04:00
|
|
|
function listenTLSNonExistentCertKeyFiles(): void {
|
2019-10-21 14:38:28 -04:00
|
|
|
const options = {
|
|
|
|
hostname: "localhost",
|
2020-05-11 14:49:19 -04:00
|
|
|
port: 3500,
|
2019-10-21 14:38:28 -04:00
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
2019-10-21 14:38:28 -04:00
|
|
|
};
|
|
|
|
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows(() => {
|
2020-04-24 17:29:14 -04:00
|
|
|
Deno.listenTls({
|
2019-10-21 14:38:28 -04:00
|
|
|
...options,
|
2020-03-28 13:03:49 -04:00
|
|
|
certFile: "./non/existent/file",
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Deno.errors.NotFound);
|
2019-10-21 14:38:28 -04:00
|
|
|
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows(() => {
|
2020-04-24 17:29:14 -04:00
|
|
|
Deno.listenTls({
|
2019-10-21 14:38:28 -04:00
|
|
|
...options,
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "./non/existent/file",
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Deno.errors.NotFound);
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2019-10-21 14:38:28 -04:00
|
|
|
);
|
|
|
|
|
2020-03-20 09:38:34 -04:00
|
|
|
unitTest({ perms: { net: true } }, function listenTLSNoReadPerm(): void {
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows(() => {
|
2020-04-24 17:29:14 -04:00
|
|
|
Deno.listenTls({
|
2020-03-20 09:38:34 -04:00
|
|
|
hostname: "localhost",
|
2020-05-11 14:49:19 -04:00
|
|
|
port: 3500,
|
2020-03-20 09:38:34 -04:00
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
2020-03-20 09:38:34 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Deno.errors.PermissionDenied);
|
2020-03-20 09:38:34 -04:00
|
|
|
});
|
2019-10-21 14:38:28 -04:00
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(
|
|
|
|
{
|
2020-03-28 13:03:49 -04:00
|
|
|
perms: { read: true, write: true, net: true },
|
2020-03-04 11:31:14 -05:00
|
|
|
},
|
2020-03-20 09:38:34 -04:00
|
|
|
function listenTLSEmptyKeyFile(): void {
|
2019-10-21 14:38:28 -04:00
|
|
|
const options = {
|
|
|
|
hostname: "localhost",
|
2020-05-11 14:49:19 -04:00
|
|
|
port: 3500,
|
2019-10-21 14:38:28 -04:00
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
2019-10-21 14:38:28 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const testDir = Deno.makeTempDirSync();
|
|
|
|
const keyFilename = testDir + "/key.pem";
|
|
|
|
Deno.writeFileSync(keyFilename, new Uint8Array([]), {
|
2020-03-28 13:03:49 -04:00
|
|
|
mode: 0o666,
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
|
|
|
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows(() => {
|
2020-04-24 17:29:14 -04:00
|
|
|
Deno.listenTls({
|
2019-10-21 14:38:28 -04:00
|
|
|
...options,
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: keyFilename,
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Error);
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2019-10-21 14:38:28 -04:00
|
|
|
);
|
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, write: true, net: true } },
|
2020-03-20 09:38:34 -04:00
|
|
|
function listenTLSEmptyCertFile(): void {
|
2019-10-21 14:38:28 -04:00
|
|
|
const options = {
|
|
|
|
hostname: "localhost",
|
2020-05-11 14:49:19 -04:00
|
|
|
port: 3500,
|
2019-10-21 14:38:28 -04:00
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
2019-10-21 14:38:28 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
const testDir = Deno.makeTempDirSync();
|
|
|
|
const certFilename = testDir + "/cert.crt";
|
|
|
|
Deno.writeFileSync(certFilename, new Uint8Array([]), {
|
2020-03-28 13:03:49 -04:00
|
|
|
mode: 0o666,
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
|
|
|
|
2020-06-24 18:57:08 -04:00
|
|
|
assertThrows(() => {
|
2020-04-24 17:29:14 -04:00
|
|
|
Deno.listenTls({
|
2019-10-21 14:38:28 -04:00
|
|
|
...options,
|
2020-03-28 13:03:49 -04:00
|
|
|
certFile: certFilename,
|
2019-10-21 14:38:28 -04:00
|
|
|
});
|
2020-06-24 18:57:08 -04:00
|
|
|
}, Error);
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2019-10-21 14:38:28 -04:00
|
|
|
);
|
|
|
|
|
2020-03-04 11:31:14 -05:00
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function dialAndListenTLS(): Promise<void> {
|
2020-11-26 11:22:36 -05:00
|
|
|
const resolvable = deferred();
|
2020-03-04 11:31:14 -05:00
|
|
|
const hostname = "localhost";
|
2020-05-11 14:49:19 -04:00
|
|
|
const port = 3500;
|
2020-03-04 11:31:14 -05:00
|
|
|
|
2020-04-24 17:29:14 -04:00
|
|
|
const listener = Deno.listenTls({
|
2020-03-04 11:31:14 -05:00
|
|
|
hostname,
|
|
|
|
port,
|
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
2020-03-28 13:03:49 -04:00
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
2020-03-04 11:31:14 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
const response = encoder.encode(
|
2020-07-14 15:24:17 -04:00
|
|
|
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
|
2020-03-04 11:31:14 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
listener.accept().then(
|
|
|
|
async (conn): Promise<void> => {
|
|
|
|
assert(conn.remoteAddr != null);
|
|
|
|
assert(conn.localAddr != null);
|
|
|
|
await conn.write(response);
|
|
|
|
// TODO(bartlomieju): this might be a bug
|
|
|
|
setTimeout(() => {
|
|
|
|
conn.close();
|
|
|
|
resolvable.resolve();
|
|
|
|
}, 0);
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2020-03-04 11:31:14 -05:00
|
|
|
);
|
|
|
|
|
2020-04-24 17:29:14 -04:00
|
|
|
const conn = await Deno.connectTls({
|
2020-03-04 11:31:14 -05:00
|
|
|
hostname,
|
|
|
|
port,
|
2020-03-28 13:03:49 -04:00
|
|
|
certFile: "cli/tests/tls/RootCA.pem",
|
2020-03-04 11:31:14 -05:00
|
|
|
});
|
|
|
|
assert(conn.rid > 0);
|
|
|
|
const w = new BufWriter(conn);
|
|
|
|
const r = new BufReader(conn);
|
|
|
|
const body = `GET / HTTP/1.1\r\nHost: ${hostname}:${port}\r\n\r\n`;
|
|
|
|
const writeResult = await w.write(encoder.encode(body));
|
|
|
|
assertEquals(body.length, writeResult);
|
|
|
|
await w.flush();
|
|
|
|
const tpr = new TextProtoReader(r);
|
|
|
|
const statusLine = await tpr.readLine();
|
2020-04-28 12:40:43 -04:00
|
|
|
assert(statusLine !== null, `line must be read: ${String(statusLine)}`);
|
2020-03-04 11:31:14 -05:00
|
|
|
const m = statusLine.match(/^(.+?) (.+?) (.+?)$/);
|
|
|
|
assert(m !== null, "must be matched");
|
|
|
|
const [_, proto, status, ok] = m;
|
|
|
|
assertEquals(proto, "HTTP/1.1");
|
|
|
|
assertEquals(status, "200");
|
|
|
|
assertEquals(ok, "OK");
|
|
|
|
const headers = await tpr.readMIMEHeader();
|
2020-04-28 12:40:43 -04:00
|
|
|
assert(headers !== null);
|
2020-03-04 11:31:14 -05:00
|
|
|
const contentLength = parseInt(headers.get("content-length")!);
|
|
|
|
const bodyBuf = new Uint8Array(contentLength);
|
|
|
|
await r.readFull(bodyBuf);
|
|
|
|
assertEquals(decoder.decode(bodyBuf), "Hello World\n");
|
|
|
|
conn.close();
|
|
|
|
listener.close();
|
|
|
|
await resolvable;
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2020-03-04 11:31:14 -05:00
|
|
|
);
|
2020-04-18 11:21:20 -04:00
|
|
|
|
2021-01-14 23:32:27 -05:00
|
|
|
async function tlsPair(port: number): Promise<[Deno.Conn, Deno.Conn]> {
|
|
|
|
const listener = Deno.listenTls({
|
|
|
|
hostname: "localhost",
|
|
|
|
port,
|
|
|
|
certFile: "cli/tests/tls/localhost.crt",
|
|
|
|
keyFile: "cli/tests/tls/localhost.key",
|
|
|
|
});
|
|
|
|
|
|
|
|
const acceptPromise = listener.accept();
|
|
|
|
const connectPromise = Deno.connectTls({
|
|
|
|
hostname: "localhost",
|
|
|
|
port,
|
|
|
|
certFile: "cli/tests/tls/RootCA.pem",
|
|
|
|
});
|
|
|
|
const connections = await Promise.all([acceptPromise, connectPromise]);
|
|
|
|
|
|
|
|
listener.close();
|
|
|
|
|
|
|
|
return connections;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function sendCloseWrite(conn: Deno.Conn): Promise<void> {
|
|
|
|
const buf = new Uint8Array(1024);
|
|
|
|
let n: number | null;
|
|
|
|
|
|
|
|
// Send 1.
|
|
|
|
n = await conn.write(new Uint8Array([1]));
|
|
|
|
assertStrictEquals(n, 1);
|
|
|
|
|
|
|
|
// Send EOF.
|
|
|
|
await conn.closeWrite();
|
|
|
|
|
|
|
|
// Receive 2.
|
|
|
|
n = await conn.read(buf);
|
|
|
|
assertStrictEquals(n, 1);
|
|
|
|
assertStrictEquals(buf[0], 2);
|
|
|
|
|
|
|
|
conn.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
async function receiveCloseWrite(conn: Deno.Conn): Promise<void> {
|
|
|
|
const buf = new Uint8Array(1024);
|
|
|
|
let n: number | null;
|
|
|
|
|
|
|
|
// Receive 1.
|
|
|
|
n = await conn.read(buf);
|
|
|
|
assertStrictEquals(n, 1);
|
|
|
|
assertStrictEquals(buf[0], 1);
|
|
|
|
|
|
|
|
// Receive EOF.
|
|
|
|
n = await conn.read(buf);
|
|
|
|
assertStrictEquals(n, null);
|
|
|
|
|
|
|
|
// Send 2.
|
|
|
|
n = await conn.write(new Uint8Array([2]));
|
|
|
|
assertStrictEquals(n, 1);
|
|
|
|
|
|
|
|
conn.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
async function sendAlotReceiveNothing(conn: Deno.Conn): Promise<void> {
|
|
|
|
// Start receive op.
|
|
|
|
const readBuf = new Uint8Array(1024);
|
|
|
|
const readPromise = conn.read(readBuf);
|
|
|
|
|
|
|
|
// Send 1 MB of data.
|
|
|
|
const writeBuf = new Uint8Array(1 << 20);
|
|
|
|
writeBuf.fill(42);
|
|
|
|
await conn.write(writeBuf);
|
|
|
|
|
|
|
|
// Send EOF.
|
|
|
|
await conn.closeWrite();
|
|
|
|
|
|
|
|
// Close the connection.
|
|
|
|
conn.close();
|
|
|
|
|
|
|
|
// Read op should be canceled.
|
|
|
|
await assertThrowsAsync(
|
|
|
|
async () => await readPromise,
|
|
|
|
Deno.errors.Interrupted,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function receiveAlotSendNothing(conn: Deno.Conn): Promise<void> {
|
|
|
|
const readBuf = new Uint8Array(1024);
|
|
|
|
let n: number | null;
|
|
|
|
|
|
|
|
// Receive 1 MB of data.
|
|
|
|
for (let nread = 0; nread < 1 << 20; nread += n!) {
|
|
|
|
n = await conn.read(readBuf);
|
|
|
|
assertStrictEquals(typeof n, "number");
|
|
|
|
assert(n! > 0);
|
|
|
|
assertStrictEquals(readBuf[0], 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the connection, without sending anything at all.
|
|
|
|
conn.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function tlsServerStreamHalfClose(): Promise<void> {
|
|
|
|
const [serverConn, clientConn] = await tlsPair(3501);
|
|
|
|
await Promise.all([
|
|
|
|
sendCloseWrite(serverConn),
|
|
|
|
receiveCloseWrite(clientConn),
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function tlsClientStreamHalfClose(): Promise<void> {
|
|
|
|
const [serverConn, clientConn] = await tlsPair(3502);
|
|
|
|
await Promise.all([
|
|
|
|
sendCloseWrite(clientConn),
|
|
|
|
receiveCloseWrite(serverConn),
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function tlsServerStreamCancelRead(): Promise<void> {
|
|
|
|
const [serverConn, clientConn] = await tlsPair(3503);
|
|
|
|
await Promise.all([
|
|
|
|
sendAlotReceiveNothing(serverConn),
|
|
|
|
receiveAlotSendNothing(clientConn),
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
|
|
|
async function tlsClientStreamCancelRead(): Promise<void> {
|
|
|
|
const [serverConn, clientConn] = await tlsPair(3504);
|
|
|
|
await Promise.all([
|
|
|
|
sendAlotReceiveNothing(clientConn),
|
|
|
|
receiveAlotSendNothing(serverConn),
|
|
|
|
]);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2020-04-18 11:21:20 -04:00
|
|
|
unitTest(
|
|
|
|
{ perms: { read: true, net: true } },
|
2020-04-24 17:29:14 -04:00
|
|
|
async function startTls(): Promise<void> {
|
2020-04-18 11:21:20 -04:00
|
|
|
const hostname = "smtp.gmail.com";
|
|
|
|
const port = 587;
|
|
|
|
const encoder = new TextEncoder();
|
|
|
|
|
|
|
|
let conn = await Deno.connect({
|
|
|
|
hostname,
|
|
|
|
port,
|
|
|
|
});
|
|
|
|
|
|
|
|
let writer = new BufWriter(conn);
|
|
|
|
let reader = new TextProtoReader(new BufReader(conn));
|
|
|
|
|
2020-04-28 12:40:43 -04:00
|
|
|
let line: string | null = (await reader.readLine()) as string;
|
2020-04-18 11:21:20 -04:00
|
|
|
assert(line.startsWith("220"));
|
|
|
|
|
|
|
|
await writer.write(encoder.encode(`EHLO ${hostname}\r\n`));
|
|
|
|
await writer.flush();
|
|
|
|
|
|
|
|
while ((line = (await reader.readLine()) as string)) {
|
|
|
|
assert(line.startsWith("250"));
|
|
|
|
if (line.startsWith("250 ")) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
await writer.write(encoder.encode("STARTTLS\r\n"));
|
|
|
|
await writer.flush();
|
|
|
|
|
|
|
|
line = await reader.readLine();
|
|
|
|
|
|
|
|
// Received the message that the server is ready to establish TLS
|
|
|
|
assertEquals(line, "220 2.0.0 Ready to start TLS");
|
|
|
|
|
2020-04-24 17:29:14 -04:00
|
|
|
conn = await Deno.startTls(conn, { hostname });
|
2020-04-18 11:21:20 -04:00
|
|
|
writer = new BufWriter(conn);
|
|
|
|
reader = new TextProtoReader(new BufReader(conn));
|
|
|
|
|
|
|
|
// After that use TLS communication again
|
|
|
|
await writer.write(encoder.encode(`EHLO ${hostname}\r\n`));
|
|
|
|
await writer.flush();
|
|
|
|
|
|
|
|
while ((line = (await reader.readLine()) as string)) {
|
|
|
|
assert(line.startsWith("250"));
|
|
|
|
if (line.startsWith("250 ")) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn.close();
|
2020-07-14 15:24:17 -04:00
|
|
|
},
|
2020-04-18 11:21:20 -04:00
|
|
|
);
|