fixed X11 port detection for UNIX sockets - fixes #4818, fixes #2003

This commit is contained in:
Eugene Pankov 2021-10-24 22:23:50 +02:00
parent c25d4bd768
commit 8e2ffa1654
No known key found for this signature in database
GPG key ID: 5896FCBBDD1CF4F4
2 changed files with 77 additions and 25 deletions

View file

@ -9,7 +9,7 @@ import { Injector, NgZone } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, FileProvidersService, HostAppService, NotificationsService, Platform, PlatformService, wrapPromise, PromptModalComponent, LogService } from 'tabby-core'
import { BaseSession } from 'tabby-terminal'
import { Socket, createConnection } from 'net'
import { Socket } from 'net'
import { Client, ClientChannel, SFTPWrapper } from 'ssh2'
import { Subject, Observable } from 'rxjs'
import { ProxyCommandStream } from '../services/ssh.service'
@ -18,6 +18,7 @@ import { promisify } from 'util'
import { SFTPSession } from './sftp'
import { ALGORITHM_BLACKLIST, SSHAlgorithmType, PortForwardType, SSHProfile } from '../api'
import { ForwardedPort } from './forwards'
import { X11Socket } from './x11'
const WINDOWS_OPENSSH_AGENT_PIPE = '\\\\.\\pipe\\openssh-ssh-agent'
@ -359,41 +360,35 @@ export class SSHSession extends BaseSession {
})
})
this.ssh.on('x11', (details, accept, reject) => {
this.ssh.on('x11', async (details, accept, reject) => {
this.logger.info(`Incoming X11 connection from ${details.srcIP}:${details.srcPort}`)
const displaySpec = process.env.DISPLAY ?? ':0'
const displaySpec = process.env.DISPLAY ?? 'localhost:0'
this.logger.debug(`Trying display ${displaySpec}`)
const xHost = displaySpec.split(':')[0]
const xDisplay = parseInt(displaySpec.split(':')[1].split('.')[0] || '0')
const xPort = xDisplay < 100 ? xDisplay + 6000 : xDisplay
const socket = displaySpec.startsWith('/') ? createConnection(displaySpec) : new Socket()
if (!displaySpec.startsWith('/')) {
socket.connect(xPort, xHost)
}
socket.on('error', e => {
const socket = new X11Socket()
try {
const x11Stream = await socket.connect(displaySpec)
this.logger.info('Connection forwarded')
const stream = accept()
stream.pipe(x11Stream)
x11Stream.pipe(stream)
stream.on('close', () => {
socket.destroy()
})
x11Stream.on('close', () => {
stream.close()
})
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
this.emitServiceMessage(colors.bgRed.black(' X ') + ` Could not connect to the X server: ${e}`)
this.emitServiceMessage(` Tabby tried to connect to ${xHost}:${xPort} based on the DISPLAY environment var (${displaySpec})`)
this.emitServiceMessage(` Tabby tried to connect to ${JSON.stringify(X11Socket.resolveDisplaySpec(displaySpec))} based on the DISPLAY environment var (${displaySpec})`)
if (process.platform === 'win32') {
this.emitServiceMessage(' To use X forwarding, you need a local X server, e.g.:')
this.emitServiceMessage(' * VcXsrv: https://sourceforge.net/projects/vcxsrv/')
this.emitServiceMessage(' * Xming: https://sourceforge.net/projects/xming/')
}
reject()
})
socket.on('connect', () => {
this.logger.info('Connection forwarded')
const stream = accept()
stream.pipe(socket)
socket.pipe(stream)
stream.on('close', () => {
socket.destroy()
})
socket.on('close', () => {
stream.close()
})
})
}
})
}

View file

@ -0,0 +1,57 @@
import { Socket, SocketConnectOpts } from 'net'
import { Subject } from 'rxjs'
export class X11Socket {
error$ = new Subject<Error>()
private socket: Socket | null = null
static resolveDisplaySpec (spec: string): SocketConnectOpts {
// eslint-disable-next-line prefer-const
let [xHost, xDisplay] = /^(.+):(\d+)(?:.(\d+))$/.exec(spec) ?? []
if (process.platform === 'win32') {
xHost ??= 'localhost'
} else {
xHost ??= 'unix'
}
if (spec.startsWith('/')) {
xHost = spec
}
const display = parseInt(xDisplay || '0')
const port = display < 100 ? display + 6000 : display
if (xHost === 'unix') {
xHost = `/tmp/.X11-unix/X${display}`
}
if (xHost.startsWith('/')) {
return {
path: xHost,
}
} else {
return {
host: xHost,
port: port,
}
}
}
connect (spec: string): Promise<Socket> {
this.socket = new Socket()
return new Promise((resolve, reject) => {
this.socket!.on('connect', () => {
resolve(this.socket!)
})
this.socket!.on('error', e => {
this.error$.next(e)
reject(e)
})
this.socket!.connect(X11Socket.resolveDisplaySpec(spec))
})
}
destroy (): void {
this.socket?.destroy()
}
}