Skip to content

Commit 011530e

Browse files
committed
Proxy TLS sockets
1 parent 8ded89e commit 011530e

2 files changed

Lines changed: 87 additions & 6 deletions

File tree

src/connection.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as cp from "child_process";
2-
import * as tls from "tls";
32

43
import { getPathFromAmdModule } from "vs/base/common/amd";
54
import { VSBuffer } from "vs/base/common/buffer";
@@ -62,8 +61,9 @@ export class ExtensionHostConnection extends Connection {
6261

6362
public constructor(protocol: Protocol, buffer: VSBuffer, private readonly log: ILogService) {
6463
super(protocol);
65-
protocol.dispose();
64+
this.protocol.dispose();
6665
this.process = this.spawn(buffer);
66+
this.protocol.getUnderlyingSocket().pause();
6767
}
6868

6969
protected dispose(): void {
@@ -89,7 +89,7 @@ export class ExtensionHostConnection extends Connection {
8989
type: "VSCODE_EXTHOST_IPC_SOCKET",
9090
initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
9191
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
92-
}, socket instanceof tls.TLSSocket ? (<any>socket)._parent : socket);
92+
}, socket);
9393
}
9494

9595
private spawn(buffer: VSBuffer): cp.ChildProcess {

src/server.ts

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import * as querystring from "querystring";
1212
import { Emitter } from "vs/base/common/event";
1313
import { sanitizeFilePath } from "vs/base/common/extpath";
1414
import { UriComponents, URI } from "vs/base/common/uri";
15+
import { generateUuid } from "vs/base/common/uuid";
1516
import { getMachineId } from 'vs/base/node/id';
1617
import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc";
17-
import { mkdirp } from "vs/base/node/pfs";
18+
import { mkdirp, rimraf } from "vs/base/node/pfs";
1819
import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner";
1920
import { IConfigurationService } from "vs/platform/configuration/common/configuration";
2021
import { ConfigurationService } from "vs/platform/configuration/node/configurationService";
@@ -56,7 +57,7 @@ import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/se
5657
import { ExtensionEnvironmentChannel, FileProviderChannel , } from "vs/server/src/channel";
5758
import { TelemetryClient } from "vs/server/src/insights";
5859
import { Protocol } from "vs/server/src/protocol";
59-
import { AuthType, getMediaMime, getUriTransformer } from "vs/server/src/util";
60+
import { AuthType, getMediaMime, getUriTransformer, tmpdir } from "vs/server/src/util";
6061

6162
export enum HttpCode {
6263
Ok = 200,
@@ -391,6 +392,11 @@ export class MainServer extends Server {
391392
private readonly services = new ServiceCollection();
392393
private readonly servicesPromise: Promise<void>;
393394

395+
public readonly _onProxyConnect = new Emitter<net.Socket>();
396+
private proxyPipe = path.join(tmpdir, "tls-proxy");
397+
private _proxyServer?: Promise<net.Server>;
398+
private readonly proxyTimeout = 5000;
399+
394400
public constructor(options: ServerOptions, args: ParsedArgs) {
395401
super(options);
396402
this.servicesPromise = this.initializeServices(args);
@@ -407,7 +413,7 @@ export class MainServer extends Server {
407413
}
408414

409415
protected async handleWebSocket(socket: net.Socket, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
410-
const protocol = new Protocol(socket, {
416+
const protocol = new Protocol(await this.createProxy(socket), {
411417
reconnectionToken: <string>parsedUrl.query.reconnectionToken || "",
412418
reconnection: parsedUrl.query.reconnection === "true",
413419
skipWebSocketFrames: parsedUrl.query.skipWebSocketFrames === "true",
@@ -592,4 +598,79 @@ export class MainServer extends Server {
592598
private async getDebugPort(): Promise<number | undefined> {
593599
return undefined;
594600
}
601+
602+
/**
603+
* Since we can't pass TLS sockets to children, use this to proxy the socket
604+
* and pass a non-TLS socket.
605+
*/
606+
private createProxy = async (socket: net.Socket): Promise<net.Socket> => {
607+
if (!(socket instanceof tls.TLSSocket)) {
608+
return socket;
609+
}
610+
611+
await this.startProxyServer();
612+
613+
return new Promise((resolve, reject) => {
614+
const timeout = setTimeout(() => {
615+
listener.dispose();
616+
socket.destroy();
617+
proxy.destroy();
618+
reject(new Error("TLS socket proxy timed out"));
619+
}, this.proxyTimeout);
620+
621+
const listener = this._onProxyConnect.event((connection) => {
622+
connection.once("data", (data) => {
623+
if (!socket.destroyed && !proxy.destroyed && data.toString() === id) {
624+
clearTimeout(timeout);
625+
listener.dispose();
626+
[[proxy, socket], [socket, proxy]].forEach(([a, b]) => {
627+
a.pipe(b);
628+
a.on("error", () => b.destroy());
629+
a.on("close", () => b.destroy());
630+
a.on("end", () => b.end());
631+
});
632+
resolve(connection);
633+
}
634+
});
635+
});
636+
637+
const id = generateUuid();
638+
const proxy = net.connect(this.proxyPipe);
639+
proxy.once("connect", () => proxy.write(id));
640+
});
641+
}
642+
643+
private async startProxyServer(): Promise<net.Server> {
644+
if (!this._proxyServer) {
645+
this._proxyServer = new Promise(async (resolve) => {
646+
this.proxyPipe = await this.findFreeSocketPath(this.proxyPipe);
647+
await mkdirp(tmpdir);
648+
await rimraf(this.proxyPipe);
649+
const proxyServer = net.createServer((p) => this._onProxyConnect.fire(p));
650+
proxyServer.once("listening", resolve);
651+
proxyServer.listen(this.proxyPipe);
652+
});
653+
}
654+
return this._proxyServer;
655+
}
656+
657+
private async findFreeSocketPath(basePath: string, maxTries: number = 100): Promise<string> {
658+
const canConnect = (path: string): Promise<boolean> => {
659+
return new Promise((resolve) => {
660+
const socket = net.connect(path);
661+
socket.once("error", () => resolve(false));
662+
socket.once("connect", () => {
663+
socket.destroy();
664+
resolve(true);
665+
});
666+
});
667+
};
668+
669+
let i = 0;
670+
let path = basePath;
671+
while (await canConnect(path) && i < maxTries) {
672+
path = `${basePath}-${++i}`;
673+
}
674+
return path;
675+
}
595676
}

0 commit comments

Comments
 (0)