@microsoft/dev-tunnels-ssh-tcp
Version:
SSH TCP extensions library for Dev Tunnels
81 lines • 4.46 kB
JavaScript
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemotePortForwarder = void 0;
const net = require("net");
const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh");
const streamForwarder_1 = require("./streamForwarder");
const remotePortConnector_1 = require("./remotePortConnector");
/**
* Receives SSH channels forwarded from a remote port and forwards them on to a local port.
*/
class RemotePortForwarder extends remotePortConnector_1.RemotePortConnector {
/* @internal */
constructor(pfs, session, remoteIPAddress, remotePort, localHost, localPort) {
super(session, remoteIPAddress, remotePort);
this.pfs = pfs;
this.localHost = localHost;
this.localPort = localPort;
}
/* @internal */
async onPortChannelOpening(request, cancellation) {
await RemotePortForwarder.forwardChannel(this.pfs, request, this.localHost, this.localPort, this.remotePort, this.trace, cancellation);
}
/* @internal */
static async forwardChannel(pfs, request, localHost, localPort, remotePort, trace, cancellation) {
const channel = request.channel;
const forwardedStream = await pfs.forwardedPortConnecting(remotePort !== null && remotePort !== void 0 ? remotePort : localPort, true, new dev_tunnels_ssh_1.SshStream(channel), cancellation);
if (!forwardedStream) {
// The event handler rejected the connection.
request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.connectFailed;
return;
}
const socket = net.createConnection({
host: localHost,
port: localPort,
// This option enables connection attempts for multiple resolved IP addresses,
// aka "Happy Eyeballs" as described in https://datatracker.ietf.org/doc/html/rfc8305.
// Effectively this enables fast connections to either 127.0.0.1 or ::1 when 'localhost'
// is specified as the hostname. Note this option is available starting with Node.js
// v18.13 and is enabled by default starting with Node.js v20.0.
autoSelectFamily: true,
// Use the minimum supported connection attempt delay when connecting to 'localhost'.
// See https://nodejs.org/api/net.html#socketconnectoptions-connectlistener
autoSelectFamilyAttemptTimeout: localHost === 'localhost' ? 10 : 250,
});
const connectCompletion = new dev_tunnels_ssh_1.PromiseCompletionSource();
const cancellationRegistration = cancellation
? cancellation.onCancellationRequested(() => socket.destroy(new Error('Cancelled.')))
: null;
try {
socket.once('connect', () => {
connectCompletion.resolve();
});
socket.once('error', (e) => {
connectCompletion.reject(e);
});
await connectCompletion.promise;
}
catch (e) {
if (!(e instanceof Error) || (cancellation === null || cancellation === void 0 ? void 0 : cancellation.isCancellationRequested)) {
throw e;
}
trace(dev_tunnels_ssh_1.TraceLevel.Warning, dev_tunnels_ssh_1.SshTraceEventIds.portForwardConnectionFailed, `PortForwardingService connection ` +
`to ${localHost}:${localPort} failed: ${e.message}`, e);
request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.connectFailed;
request.failureDescription = e.message;
}
finally {
cancellationRegistration === null || cancellationRegistration === void 0 ? void 0 : cancellationRegistration.dispose();
}
// TODO: Set socket options?
const streamForwarder = new streamForwarder_1.StreamForwarder(socket, forwardedStream, channel.session.trace);
trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.portForwardConnectionOpened, `${channel.session} PortForwardingService forwarded channel ` +
`#${channel.channelId} connection to ${localHost}:${localPort}.`);
pfs.streamForwarders.push(streamForwarder);
}
}
exports.RemotePortForwarder = RemotePortForwarder;
//# sourceMappingURL=remotePortForwarder.js.map
;