UNPKG

@microsoft/dev-tunnels-ssh-tcp

Version:

SSH TCP extensions library for Dev Tunnels

612 lines 35.7 kB
"use strict"; // // Copyright (c) Microsoft Corporation. All rights reserved. // var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var PortForwardingService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.PortForwardingService = void 0; const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh"); const vscode_jsonrpc_1 = require("vscode-jsonrpc"); const forwardedPort_1 = require("../events/forwardedPort"); const forwardedPortsCollection_1 = require("../events/forwardedPortsCollection"); const ipAddressConversions_1 = require("../ipAddressConversions"); const portForwardChannelOpenMessage_1 = require("../messages/portForwardChannelOpenMessage"); const portForwardRequestMessage_1 = require("../messages/portForwardRequestMessage"); const portForwardSuccessMessage_1 = require("../messages/portForwardSuccessMessage"); const tcpListenerFactory_1 = require("../tcpListenerFactory"); const portForwardMessageFactory_1 = require("../portForwardMessageFactory"); const localPortForwarder_1 = require("./localPortForwarder"); const remotePortForwarder_1 = require("./remotePortForwarder"); const remotePortStreamer_1 = require("./remotePortStreamer"); const forwardedPortEventArgs_1 = require("../events/forwardedPortEventArgs"); /** * Implements the standard SSH port-forwarding protocol. * @example * Use `SshSessionConfiguration.addService()` on both client and server side configurations * to add the `PortForwardingService` type before attempting to call methods on the service. * Then use `SshSession.activateService()` to get the service instance: * * const config = new SshSessionConfiguration(); * config.addService(PortForwardingService); * const client = new SshClient(config); * const session = await client.openSession(host, port); * await session.authenticate(clientCredentials); * const pfs = session.activateService(PortForwardingService); * const forwarder = pfs.forwardToRemotePort('::', 3000); */ let PortForwardingService = PortForwardingService_1 = class PortForwardingService extends dev_tunnels_ssh_1.SshService { /* @internal */ constructor(session) { super(session); /** * Maps from FORWARDED port number to the object that manages listening for incoming * connections for that port and forwarding them through the session. * * Note the actual local source port number used may be different from the forwarded port * number if the local TCP listener factory chose a different port. The forwarded port number * is used to identify the port in any messages exchanged between client and server. */ this.localForwarders = new Map(); /** * Maps from FORWARDED port numbers to the object that manages relaying forwarded connections * from the session to a local port. * * Note the actual local destination port number used may be different from the forwarded port * number. The forwarded port number is used to identify the port in any messages exchanged * between client and server. */ this.remoteConnectors = new Map(); /* @internal */ this.streamForwarders = []; /** * Gets or sets a value that controls whether the port-forwarding service listens on * local TCP sockets to accept connections for ports that are forwarded from the remote side. * * The default is true. * * This property is typically initialized before connecting a session (if not keeping the * default). It may be changed at any time while the session is connected, and the new value * will affect any newly forwarded ports after that, but not previously-forwarded ports. * * Regardless of whether this is enabled, connections to forwarded ports can be made using * `connectToForwardedPort()`. */ this.acceptLocalConnectionsForForwardedPorts = true; /** * Gets or sets a value that controls whether the port-forwarding service forwards connections * to local TCP sockets. * * The default is true. * * This property is typically initialized before connecting a session (if not keeping the * default). It may be changed at any time while the session is connected, and the new value * will affect any newly forwarded ports after that, but not previously-forwarded ports. */ this.forwardConnectionsToLocalPorts = true; /** * Gets or sets a value that controls whether the port-forwarding service accepts * 'direct-tcpip' channel open requests and forwards the channel connections to the local port. * * The default is true. * * This property is typically initialized before connecting a session (if not keeping the * default). It may be changed at any time while the session is connected, and the new value * will affect any newly forwarded ports after that, but not previously-forwarded ports. * * Regardless of whether this is enabled, the remote side can open 'forwarded-tcpip' channels * to connect to ports that were explicitly forwarded by this side. */ this.acceptRemoteConnectionsForNonForwardedPorts = true; /** * Gets the collection of ports that are currently being forwarded from the remote side * to the local side. * * Ports are added to this collection when `forwardFromRemotePort()` or * `streamFromRemotePort()` is called (and the other side accepts the * 'tcpip-forward' request), and then are removed when the `RemotePortForwarder` * is disposed (which also sends a 'cancel-tcpip-forward' message). * * Each forwarded port may have 0 or more active connections (channels). * * The collection does not include direct connections initiated via * `forwardToRemotePort()` or `streamToRemotePort()`. * * Local forwarded ports may or may not have local TCP listeners automatically set up, * depending on the value of `acceptLocalConnectionsForForwardedPorts`. */ this.localForwardedPorts = new forwardedPortsCollection_1.ForwardedPortsCollection(); /** * Gets the collection of ports that are currently being forwarded from the local side * to the remote side. * * Ports are added to this collection when the port-forwarding service handles a * 'tcpip-forward' request message, and removed when it receives a 'cancel-tcpip-forward' * request message. * * Each forwarded port may have 0 or more active connections (channels). * * The collection does not include direct connections initiated via * `forwardToRemotePort()` or `streamToRemotePort()`. */ this.remoteForwardedPorts = new forwardedPortsCollection_1.ForwardedPortsCollection(); /** * 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 factory is not used when `acceptLocalConnectionsForForwardedPorts` is * set to false. */ this.tcpListenerFactory = new tcpListenerFactory_1.DefaultTcpListenerFactory(); /** * Gets or sets a factory for creating port-forwarding messages. * * A message factory enables applications to extend port-forwarding by providing custom * message subclasses that may include additional properties. */ this.messageFactory = new portForwardMessageFactory_1.DefaultPortForwardMessageFactory(); this.forwardedPortConnectingEmitter = new vscode_jsonrpc_1.Emitter(); /** * Event raised when an incoming or outgoing connection to a forwarded port is * about to be established. */ this.onForwardedPortConnecting = this.forwardedPortConnectingEmitter.event; } /* @internal */ async forwardedPortConnecting(port, isIncoming, stream, cancellation) { try { const args = new forwardedPortEventArgs_1.ForwardedPortConnectingEventArgs(port, isIncoming, stream, cancellation); this.forwardedPortConnectingEmitter.fire(args); if (args.transformPromise) { return await args.transformPromise; } } catch (e) { if (!(e instanceof Error)) throw e; this.trace(dev_tunnels_ssh_1.TraceLevel.Warning, dev_tunnels_ssh_1.SshTraceEventIds.portForwardConnectionFailed, `Forwarded port connecting event-handler failed: ${e.message}`); return null; } return stream; } async forwardFromRemotePort(remoteIPAddress, remotePort, localHostOrCancellation, localPort, cancellation) { const localHost = typeof localHostOrCancellation === 'string' ? localHostOrCancellation : '127.0.0.1'; if (typeof localPort === 'undefined') localPort = remotePort; if (!remoteIPAddress) throw new TypeError('Remote IP address is required.'); if (!Number.isInteger(remotePort) || remotePort < 0) { throw new TypeError('Remote port must be a non-negative integer.'); } if (!localHost) throw new TypeError('Local host is required.'); if (!Number.isInteger(localPort) || localPort <= 0) { throw new TypeError('Local port must be a positive integer.'); } if (this.localForwardedPorts.find((p) => p.localPort === localPort)) { throw new Error(`Local port ${localPort} is already forwarded.`); } else if (remotePort > 0 && this.localForwardedPorts.find((p) => p.remotePort === remotePort)) { throw new Error(`Remote port ${remotePort} is already forwarded.`); } const forwarder = new remotePortForwarder_1.RemotePortForwarder(this, this.session, remoteIPAddress, remotePort, localHost, localPort); const request = await this.messageFactory.createRequestMessageAsync(remotePort); if (!(await forwarder.request(request, cancellation))) { // The remote side rejected the forwarding request, or it was a duplicate request. return null; } remotePort = forwarder.remotePort; // The remote port is the port sent in the message to the other side, // so the connector is indexed on that port number, rather than the local port. // // Do not track duplicate port forwarders. (The remote side may not have detected the // duplicate if not accepting local connections.) if (this.remoteConnectors.has(remotePort)) { // Do not dispose the forwarder because that would send a message to cancel // forwarding of the port. return null; } this.remoteConnectors.set(remotePort, forwarder); const forwardedPort = new forwardedPort_1.ForwardedPort(localPort, remotePort, false); this.localForwardedPorts.addOrUpdatePort(forwardedPort); forwarder.onDisposed(() => { this.localForwardedPorts.removePort(forwardedPort); this.remoteConnectors.delete(remotePort); }); return forwarder; } async forwardToRemotePort(localIPAddress, localPort, remoteHostOrCancellation, remotePort, cancellation) { const remoteHost = typeof remoteHostOrCancellation === 'string' ? remoteHostOrCancellation : '127.0.0.1'; if (typeof remotePort === 'undefined') remotePort = localPort; if (!localIPAddress) throw new TypeError('Local IP address is required.'); if (!Number.isInteger(localPort) || localPort < 0) { throw new TypeError('Local port must be a non-negative integer.'); } if (!remoteHost) throw new TypeError('Remote host is required.'); if (!Number.isInteger(remotePort) || remotePort <= 0) { throw new TypeError('Remote port must be a positive integer.'); } if (this.localForwarders.has(remotePort)) { throw new Error(`Port ${remotePort} is already forwarded.`); } const forwarder = new localPortForwarder_1.LocalPortForwarder(this, this.session, PortForwardingService_1.reversePortForwardChannelType, localIPAddress, localPort, remoteHost, remotePort); await forwarder.startForwarding(cancellation); // The remote port is the port sent in the message to the other side, // so the forwarder is indexed on that port number, rather than the local port. this.localForwarders.set(remotePort, forwarder); forwarder.onDisposed(() => { this.localForwarders.delete(remotePort); }); return forwarder; } /** * Sends a request to the remote side to listen on a port and forward incoming connections as * SSH channels of type 'forwarded-tcpip', which will then be relayed as local streams. * * @param remoteIPAddress IP address of the interface to bind to on the remote side. * @param remotePort The remote port to listen on, or 0 to choose an available port. * (The chosen port can then be obtained via the `remotePort` property on the returned object.) * @param cancellation Cancellation token for the request; note this cannot cancel forwarding * once it has started; use the returned disposable do do that. * @returns A disposable object that when disposed will cancel forwarding the port, or `null` * if the request was rejected by the remote side, possibly because the remote port was already * in use. Handle the `onStreamOpened` event on this object to receive streams. */ async streamFromRemotePort(remoteIPAddress, remotePort, cancellation) { if (!remoteIPAddress) throw new TypeError('Remote IP address is required.'); if (!Number.isInteger(remotePort) || remotePort < 0) { throw new TypeError('Remote port must be a non-negative integer.'); } const streamer = new remotePortStreamer_1.RemotePortStreamer(this.session, remoteIPAddress, remotePort); const request = await this.messageFactory.createRequestMessageAsync(remotePort); if (!(await streamer.request(request, cancellation))) { streamer.dispose(); return null; } remotePort = streamer.remotePort; // The remote port is the port sent in the message to the other side, // so the connector is indexed on that port number. (There is no local port anyway.) this.remoteConnectors.set(remotePort, streamer); const forwardedPort = new forwardedPort_1.ForwardedPort(null, remotePort, false); this.localForwardedPorts.addOrUpdatePort(forwardedPort); streamer.onDisposed(() => { this.localForwardedPorts.removePort(forwardedPort); this.remoteConnectors.delete(remotePort); }); return streamer; } /** * Opens a stream for an SSH channel of type 'direct-tcpip' that is relayed to remote port, * regardless of whether the remote side has explicitly forwarded that port. * * @param remoteHost The destination hostname or IP address for forwarded connections, to be * resolved on the remote side. WARNING: Avoid using the hostname `localhost` as the destination * host; use `127.0.0.1` or `::1` instead. (OpenSSH does not recognize `localhost` as a valid * destination host.) * @param remotePort The destination port for the forwarded stream. (Must not be 0.) * @param cancellation Cancellation token for the request; note this cannot cancel streaming * once it has started; dipose the returned stream for that. * @returns A stream that is relayed to the remote port. * @throws `SshChannelError` if the streaming channel could not be opened, either because it * was rejected by the remote side, or the remote connection failed. */ async streamToRemotePort(remoteHost, remotePort, cancellation) { if (!remoteHost) throw new TypeError('Remote host is required.'); if (!Number.isInteger(remotePort) || remotePort <= 0) { throw new TypeError('Remote port must be a positive integer.'); } const channel = await this.openChannel(this.session, PortForwardingService_1.reversePortForwardChannelType, null, null, remoteHost, remotePort, cancellation); return new dev_tunnels_ssh_1.SshStream(channel); } /** * Opens a stream for an SSH channel of type 'forwarded-tcpip' that is relayed to a remote * port. The port must have been explicitly forwarded by the remote side. * * It may be necessary to call `waitForForwardedPort` before this method * to ensure the port is ready for connections. * * An error is thrown if the requested port could not be forwarded, possibly because it was * rejected by the remote side, or the remote connection failed. * * @param forwardedPort Remote port number that was forwarded. * @param cancellation Cancellation token for the request; note this cannot * cancel streaming once it has started; dipose the returned stream for that. * @returns A stream that is relayed to the remote forwarded port. */ async connectToForwardedPort(forwardedPort, cancellation) { if (!Number.isInteger(forwardedPort) || forwardedPort <= 0) { throw new TypeError('Forwarded port must be a positive integer.'); } const channel = await this.openChannel(this.session, PortForwardingService_1.portForwardChannelType, null, null, '127.0.0.1', forwardedPort, cancellation); const forwardedStream = await this.forwardedPortConnecting(forwardedPort, false, new dev_tunnels_ssh_1.SshStream(channel), cancellation); if (!forwardedStream) { channel.close().catch((e) => { }); throw new dev_tunnels_ssh_1.SshChannelError('The connection to the forwarded port was rejected by the connecting event-handler.'); } return forwardedStream; } /** * Waits asynchronously for the remote side to forward an expected port number. * * A common pattern for some applications may be to call this method just before * `ConnectToForwardedPortAsync`. * * @param forwardedPort Port number that is expected to be forwarded. * @param cancellation Token that can be used to cancel waiting. * @returns A promise that completes when the expected port number has been forwarded. */ async waitForForwardedPort(forwardedPort, cancellation) { if (this.remoteForwardedPorts.find((p) => p.remotePort === forwardedPort)) { // It's already forwarded, so there's no need to wait. return; } const waitCompletion = new dev_tunnels_ssh_1.PromiseCompletionSource(); let cancellationRegistration; if (cancellation) { cancellationRegistration = cancellation.onCancellationRequested(() => waitCompletion.reject(new dev_tunnels_ssh_1.CancellationError())); } let portAddedRegistration; let sessionClosedRegistration; try { portAddedRegistration = this.remoteForwardedPorts.onPortAdded((e) => { if (e.port.remotePort === forwardedPort) { waitCompletion.resolve(); } }); sessionClosedRegistration = this.session.onClosed(() => { waitCompletion.reject(new dev_tunnels_ssh_1.ObjectDisposedError('The session was closed.')); }); await waitCompletion.promise; } finally { portAddedRegistration === null || portAddedRegistration === void 0 ? void 0 : portAddedRegistration.dispose(); sessionClosedRegistration === null || sessionClosedRegistration === void 0 ? void 0 : sessionClosedRegistration.dispose(); cancellationRegistration === null || cancellationRegistration === void 0 ? void 0 : cancellationRegistration.dispose(); } } async onSessionRequest(request, cancellation) { if (!request) throw new TypeError('Request is required.'); else if (request.requestType !== PortForwardingService_1.portForwardRequestType && request.requestType !== PortForwardingService_1.cancelPortForwardRequestType) { throw new Error(`Unexpected request type: ${request.requestType}`); } const portForwardRequest = request.request.convertTo(new portForwardRequestMessage_1.PortForwardRequestMessage()); const localIPAddress = ipAddressConversions_1.IPAddressConversions.fromSshAddress(portForwardRequest.addressToBind); if (request.requestType === PortForwardingService_1.portForwardRequestType && portForwardRequest.port !== 0 && this.localForwarders.has(portForwardRequest.port)) { // The port is already forwarded, so a failure response will be returned. This may happen // when re-connecting, to ensure the state of forwarded ports is consistent. // // Note duplicate ports are not detected here when AcceptLocalConnectionsForForwardedPorts // is false; in that case duplicate port requests may succeed (if authorized below), though // they don't really do anything. const message = `PortForwardingService port ${portForwardRequest.port} is already forwarded.`; this.session.trace(dev_tunnels_ssh_1.TraceLevel.Verbose, dev_tunnels_ssh_1.SshTraceEventIds.portForwardRequestInvalid, message); request.isAuthorized = false; return; } const args = new dev_tunnels_ssh_1.SshRequestEventArgs(request.requestType, portForwardRequest, this.session.principal); await super.onSessionRequest(args, cancellation); let response; let localPort = null; if (args.isAuthorized) { if (request.requestType === PortForwardingService_1.portForwardRequestType) { try { localPort = await this.startForwarding(localIPAddress, portForwardRequest.port, cancellation); } catch (e) { // The error is already traced. } if (localPort !== null) { // The chosen local port may be different from the requested port. Use the // requested port in the response, unless the request was for a random port. const forwardedPort = portForwardRequest.port === 0 ? localPort : portForwardRequest.port; const portResponse = await this.messageFactory.createSuccessMessageAsync(forwardedPort); portResponse.port = forwardedPort; response = portResponse; } } else if (request.requestType === PortForwardingService_1.cancelPortForwardRequestType) { if (await this.cancelForwarding(portForwardRequest.port, cancellation)) { response = new dev_tunnels_ssh_1.SessionRequestSuccessMessage(); } } } request.responsePromise = Promise.resolve(response !== null && response !== void 0 ? response : new dev_tunnels_ssh_1.SessionRequestFailureMessage()); // Add to the collection (and raise event) after sending the response, // to ensure event-handlers can immediately open a channel. if (response instanceof portForwardSuccessMessage_1.PortForwardSuccessMessage) { const forwardedPort = new forwardedPort_1.ForwardedPort(localPort !== null && localPort !== void 0 ? localPort : response.port, response.port, true); this.remoteForwardedPorts.addOrUpdatePort(forwardedPort); } } async startForwarding(localIPAddress, remotePort, cancellation) { if (typeof remotePort !== 'number') throw new TypeError('Remote port must be an integer.'); if (this.acceptLocalConnectionsForForwardedPorts) { // The local port is initially set to the remote port, but it may change // when starting forwarding, if there was a conflict. let localPort = remotePort; const forwarder = new localPortForwarder_1.LocalPortForwarder(this, this.session, PortForwardingService_1.portForwardChannelType, localIPAddress, localPort, undefined, remotePort === 0 ? undefined : remotePort); await forwarder.startForwarding(cancellation); localPort = forwarder.localPort; if (remotePort === 0) { // The other side requested a random port. Reply with the chosen port number. remotePort = localPort; } if (this.localForwarders.has(remotePort)) { // The forwarder (TCP listener factory) chose a port that is already forwarded. // This can happen (though its' very unlikely) if a random port was requested. // Returning null here causes the forward request to be rejected. forwarder.dispose(); return null; } // The remote port is the port referenced in exchanged messages, // so the forwarder is indexed on that port number, rather than the local port. this.localForwarders.set(remotePort, forwarder); localPort = forwarder.localPort; forwarder.onDisposed(() => { const forwardedPort = new forwardedPort_1.ForwardedPort(localPort, remotePort, true); this.remoteForwardedPorts.removePort(forwardedPort); this.localForwarders.delete(remotePort); }); return localPort; } else if (remotePort !== 0) { return remotePort; } else { return null; } } async cancelForwarding(forwardedPort, cancellation) { const forwarder = this.localForwarders.get(forwardedPort); if (forwarder) { this.localForwarders.delete(forwardedPort); forwarder.dispose(); return true; } const port = new forwardedPort_1.ForwardedPort(forwardedPort, forwardedPort, true); if (this.remoteForwardedPorts.removePort(port)) { return true; } return false; } async onChannelOpening(request, cancellation) { var _a; if (!request) throw new TypeError('Request is required.'); const channelType = request.request.channelType; if (channelType !== PortForwardingService_1.portForwardChannelType && channelType !== PortForwardingService_1.reversePortForwardChannelType) { request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.unknownChannelType; return; } let remoteConnector = null; const portForwardMessage = request.request instanceof portForwardChannelOpenMessage_1.PortForwardChannelOpenMessage ? request.request : request.request.convertTo(new portForwardChannelOpenMessage_1.PortForwardChannelOpenMessage()); if (request.isRemoteRequest) { if (channelType === PortForwardingService_1.portForwardChannelType) { const remoteIPAddress = ipAddressConversions_1.IPAddressConversions.fromSshAddress(portForwardMessage.host); const remoteEndPoint = `${remoteIPAddress}:${portForwardMessage.port}`; remoteConnector = (_a = this.remoteConnectors.get(portForwardMessage.port)) !== null && _a !== void 0 ? _a : null; if (!remoteConnector) { this.trace(dev_tunnels_ssh_1.TraceLevel.Error, dev_tunnels_ssh_1.SshTraceEventIds.portForwardRequestInvalid, 'PortForwardingService received forwarding channel ' + `for ${remoteEndPoint} that was not requested.`); request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.connectFailed; request.failureDescription = 'Forwarding channel was not requested.'; return; } } else if (!this.acceptRemoteConnectionsForNonForwardedPorts) { const errorMessage = 'The session has disabled connections to non-forwarded ports.'; this.session.trace(dev_tunnels_ssh_1.TraceLevel.Warning, dev_tunnels_ssh_1.SshTraceEventIds.portForwardChannelOpenFailed, errorMessage); request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.administrativelyProhibited; request.failureDescription = errorMessage; return; } } const portForwardRequest = new dev_tunnels_ssh_1.SshChannelOpeningEventArgs(portForwardMessage, request.channel, request.isRemoteRequest); await super.onChannelOpening(portForwardRequest, cancellation); request.failureReason = portForwardRequest.failureReason; request.failureDescription = portForwardRequest.failureDescription; request.openingPromise = portForwardRequest.openingPromise; if (request.failureReason === dev_tunnels_ssh_1.SshChannelOpenFailureReason.none && request.isRemoteRequest && this.forwardConnectionsToLocalPorts) { if (remoteConnector) { // The forwarding was initiated by this session. await remoteConnector.onPortChannelOpening(request, cancellation); const localPort = remoteConnector instanceof remotePortForwarder_1.RemotePortForwarder ? remoteConnector.localPort : null; const remotePort = remoteConnector instanceof remotePortForwarder_1.RemotePortForwarder ? remoteConnector.remotePort : portForwardMessage.port; const forwardedPort = new forwardedPort_1.ForwardedPort(localPort, remotePort, false); this.localForwardedPorts.addChannel(forwardedPort, request.channel); } else { // THe forwarding was initiated by the remote session. await remotePortForwarder_1.RemotePortForwarder.forwardChannel(this, request, portForwardMessage.host, portForwardMessage.port, portForwardMessage.port, this.trace, cancellation); if (request.failureReason !== dev_tunnels_ssh_1.SshChannelOpenFailureReason.none) { await request.channel.close(cancellation); } } } } /* @internal */ async openChannel(session, channelType, originatorIPAddress, originatorPort, host, port, cancellation) { let forwardedPort = undefined; if (channelType === PortForwardingService_1.portForwardChannelType) { forwardedPort = this.remoteForwardedPorts.find((p) => p.remotePort === port || (p.remotePort === null && p.localPort === port)); if (!forwardedPort) { throw new Error(`Port ${port} is not being forwarded.`); } } const openMessage = await this.messageFactory.createChannelOpenMessageAsync(port); openMessage.channelType = channelType; openMessage.originatorIPAddress = originatorIPAddress !== null && originatorIPAddress !== void 0 ? originatorIPAddress : ''; openMessage.originatorPort = originatorPort !== null && originatorPort !== void 0 ? originatorPort : 0; openMessage.host = host; openMessage.port = port; const trace = this.session.trace; let channel; try { channel = await session.openChannel(openMessage, null, cancellation); trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.portForwardChannelOpened, `PortForwardingService opened ${channelType} channel #${channel.channelId} for ${host}:${port}.`); } catch (e) { if (!(e instanceof Error)) throw e; trace(dev_tunnels_ssh_1.TraceLevel.Error, dev_tunnels_ssh_1.SshTraceEventIds.portForwardChannelOpenFailed, `PortForwardingService failed to open ${channelType} channel for ${host}:${port}: ${e.message}`, e); throw e; } if (channelType === PortForwardingService_1.portForwardChannelType) { this.remoteForwardedPorts.addChannel(forwardedPort, channel); } return channel; } dispose() { // Do not dispose StreamForwarder objects here, since they may be transferred to // other sessions when reconnecting. The StreamForwarders will self-dispose when // their underlying transport streams are closed. const disposables = [ ...this.localForwarders.values(), ...this.remoteConnectors.values(), ]; this.streamForwarders.splice(0, this.streamForwarders.length); this.localForwarders.clear(); this.remoteConnectors.clear(); for (const disposable of disposables) { disposable.dispose(); } super.dispose(); } }; PortForwardingService.portForwardRequestType = 'tcpip-forward'; PortForwardingService.cancelPortForwardRequestType = 'cancel-tcpip-forward'; PortForwardingService.portForwardChannelType = 'forwarded-tcpip'; PortForwardingService.reversePortForwardChannelType = 'direct-tcpip'; PortForwardingService = PortForwardingService_1 = __decorate([ (0, dev_tunnels_ssh_1.serviceActivation)({ sessionRequest: PortForwardingService_1.portForwardRequestType }), (0, dev_tunnels_ssh_1.serviceActivation)({ sessionRequest: PortForwardingService_1.cancelPortForwardRequestType }), (0, dev_tunnels_ssh_1.serviceActivation)({ channelType: PortForwardingService_1.portForwardChannelType }), (0, dev_tunnels_ssh_1.serviceActivation)({ channelType: PortForwardingService_1.reversePortForwardChannelType }) ], PortForwardingService); exports.PortForwardingService = PortForwardingService; //# sourceMappingURL=portForwardingService.js.map