@microsoft/dev-tunnels-ssh-tcp
Version:
SSH TCP extensions library for Dev Tunnels
112 lines • 5.31 kB
JavaScript
;
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshServer = void 0;
const vscode_jsonrpc_1 = require("vscode-jsonrpc");
const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh");
const tcpListenerFactory_1 = require("./tcpListenerFactory");
/**
* Enables accepting SSH sessions on a TCP socket.
*
* It's possible to create an `SshServerSession` over any `Stream` instance;
* this class is merely a convenient helper that manages creating sessions
* over Node.js TCP `Socket`s from incoming connections.
*/
class SshServer {
constructor(config) {
this.config = config;
this.sessions = [];
/**
* Gets or sets a function that handles trace messages associated with the server sessions.
*
* By default, no messages are traced. To enable tracing, set this property to a function
* that routes the message to console.log, a file, or anywhere else.
*
* @param level The level of message being traced: error, warning, info, or verbose.
* @param eventId An integer that identifies the type of event. Normally this is one of
* the values from `SshTraceEventIds`, but extensions may define additional event IDs.
* @param msg A description of the event (non-localized).
* @param err Optional `Error` object associated with the event, often included with
* warning or error events. While the `Error.message` property is typically included as
* (part of) the `msg` parameter, the error object may contain additional useful context
* such as the stack trace.
*/
this.trace = (level, eventId, msg, err) => { };
this.errorEmitter = new vscode_jsonrpc_1.Emitter();
this.onError = this.errorEmitter.event;
this.sessionOpenedEmitter = new vscode_jsonrpc_1.Emitter();
this.onSessionOpened = this.sessionOpenedEmitter.event;
this.credentials = { publicKeys: [] };
/**
* Gets or sets a factory for creating TCP listeners.
*
* Applications may override this factory to provide custom logic for selecting
* local port numbers to listen on for port-forwarding.
*/
this.tcpListenerFactory = new tcpListenerFactory_1.DefaultTcpListenerFactory();
if (!config)
throw new TypeError('SshSessionConfiguration is required.');
if (config.protocolExtensions.includes(dev_tunnels_ssh_1.SshProtocolExtensionNames.sessionReconnect)) {
this.reconnectableSessions = [];
}
}
async acceptSessions(localPort, localAddress) {
if (!localAddress) {
localAddress = '0.0.0.0';
}
const portPrefix = localAddress === '0.0.0.0' ? 'port ' : localAddress + ':';
try {
this.tcpListener = await this.tcpListenerFactory.createTcpListener(undefined, // remotePort
localAddress, localPort, false);
}
catch (e) {
if (!(e instanceof Error))
throw e;
this.trace(dev_tunnels_ssh_1.TraceLevel.Error, dev_tunnels_ssh_1.SshTraceEventIds.serverListenFailed, `SshServer failed to listen on ${portPrefix}${localPort}: ${e.message}`, e);
throw e;
}
this.tcpListener.addListener('connection', this.acceptSession.bind(this));
this.trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.serverListening, `SshServer listening on ${portPrefix}${localPort}.`);
}
async acceptConnection(socket) {
socket.setNoDelay(true);
return new dev_tunnels_ssh_1.NodeStream(socket);
}
async acceptSession(socket) {
this.trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.serverClientConnected, 'SshServer client connected.');
const stream = await this.acceptConnection(socket);
const session = new dev_tunnels_ssh_1.SshServerSession(this.config, this.reconnectableSessions);
session.trace = this.trace;
session.credentials = this.credentials;
this.sessions.push(session);
session.onClosed((e) => {
const sessionIndex = this.sessions.indexOf(session);
if (sessionIndex >= 0) {
this.sessions.splice(sessionIndex, 1);
}
});
this.sessionOpenedEmitter.fire(session);
try {
await session.connect(stream);
}
catch (e) {
if (!(e instanceof Error))
throw e;
if (e instanceof dev_tunnels_ssh_1.SshConnectionError) {
await session.close(e.reason || dev_tunnels_ssh_1.SshDisconnectReason.connectionLost, e.message, e);
}
else {
await session.close(dev_tunnels_ssh_1.SshDisconnectReason.protocolError, e.message, e);
}
this.errorEmitter.fire(e);
}
}
dispose() {
var _a;
(_a = this.tcpListener) === null || _a === void 0 ? void 0 : _a.close();
}
}
exports.SshServer = SshServer;
//# sourceMappingURL=sshServer.js.map