fix(rdp): parse guacd args response and send matching positional connect values
This commit is contained in:
parent
72526487c3
commit
9d3a93bea9
@ -54,9 +54,19 @@ export class GuacamoleService {
|
|||||||
// Clear the connect timeout — handshake completed
|
// Clear the connect timeout — handshake completed
|
||||||
socket.setTimeout(0);
|
socket.setTimeout(0);
|
||||||
|
|
||||||
// Phase 2: CONNECT with RDP parameters
|
// Parse the args instruction to get expected parameter names
|
||||||
const connectInstruction = this.buildConnectInstruction(params);
|
const argsInstruction = buffer.substring(0, semicolonIdx + 1);
|
||||||
this.logger.debug(`Sending connect instruction to guacd`);
|
const argNames = this.decode(argsInstruction);
|
||||||
|
|
||||||
|
// First element is the opcode ("args"), rest are parameter names
|
||||||
|
if (argNames[0] === 'args') {
|
||||||
|
argNames.shift();
|
||||||
|
}
|
||||||
|
this.logger.log(`guacd expects ${argNames.length} args: ${argNames.join(', ')}`);
|
||||||
|
|
||||||
|
// Phase 2: CONNECT — send values in the exact order guacd expects
|
||||||
|
const connectInstruction = this.buildConnectInstruction(params, argNames);
|
||||||
|
this.logger.debug(`Sending connect instruction with ${argNames.length} values`);
|
||||||
socket.write(connectInstruction);
|
socket.write(connectInstruction);
|
||||||
|
|
||||||
resolve(socket);
|
resolve(socket);
|
||||||
@ -78,31 +88,33 @@ export class GuacamoleService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildConnectInstruction(params: {
|
private buildConnectInstruction(
|
||||||
hostname: string;
|
params: {
|
||||||
port: number;
|
hostname: string;
|
||||||
username: string;
|
port: number;
|
||||||
password: string;
|
username: string;
|
||||||
domain?: string;
|
password: string;
|
||||||
width: number;
|
domain?: string;
|
||||||
height: number;
|
width: number;
|
||||||
dpi?: number;
|
height: number;
|
||||||
security?: string;
|
dpi?: number;
|
||||||
colorDepth?: number;
|
security?: string;
|
||||||
ignoreCert?: boolean;
|
colorDepth?: number;
|
||||||
}): string {
|
ignoreCert?: boolean;
|
||||||
// The connect instruction sends all RDP connection parameters as positional args.
|
},
|
||||||
// guacd expects exactly the args it listed in the "args" response to SELECT.
|
argNames: string[],
|
||||||
// We send the full standard RDP parameter set.
|
): string {
|
||||||
const args: Record<string, string> = {
|
// Map our params to guacd's expected arg names
|
||||||
hostname: params.hostname,
|
const paramMap: Record<string, string> = {
|
||||||
port: String(params.port),
|
'hostname': params.hostname,
|
||||||
username: params.username,
|
'port': String(params.port),
|
||||||
password: params.password,
|
'username': params.username,
|
||||||
width: String(params.width),
|
'password': params.password,
|
||||||
height: String(params.height),
|
'domain': params.domain || '',
|
||||||
dpi: String(params.dpi || 96),
|
'width': String(params.width),
|
||||||
security: params.security || 'any',
|
'height': String(params.height),
|
||||||
|
'dpi': String(params.dpi || 96),
|
||||||
|
'security': params.security || 'any',
|
||||||
'color-depth': String(params.colorDepth || 24),
|
'color-depth': String(params.colorDepth || 24),
|
||||||
'ignore-cert': params.ignoreCert !== false ? 'true' : 'false',
|
'ignore-cert': params.ignoreCert !== false ? 'true' : 'false',
|
||||||
'disable-audio': 'false',
|
'disable-audio': 'false',
|
||||||
@ -110,14 +122,55 @@ export class GuacamoleService {
|
|||||||
'enable-theming': 'true',
|
'enable-theming': 'true',
|
||||||
'enable-font-smoothing': 'true',
|
'enable-font-smoothing': 'true',
|
||||||
'resize-method': 'reconnect',
|
'resize-method': 'reconnect',
|
||||||
|
'server-layout': '',
|
||||||
|
'timezone': '',
|
||||||
|
'console': '',
|
||||||
|
'initial-program': '',
|
||||||
|
'client-name': 'Wraith',
|
||||||
|
'enable-full-window-drag': 'false',
|
||||||
|
'enable-desktop-composition': 'false',
|
||||||
|
'enable-menu-animations': 'false',
|
||||||
|
'disable-bitmap-caching': 'false',
|
||||||
|
'disable-offscreen-caching': 'false',
|
||||||
|
'disable-glyph-caching': 'false',
|
||||||
|
'preconnection-id': '',
|
||||||
|
'preconnection-blob': '',
|
||||||
|
'enable-sftp': 'false',
|
||||||
|
'sftp-hostname': '',
|
||||||
|
'sftp-port': '',
|
||||||
|
'sftp-username': '',
|
||||||
|
'sftp-password': '',
|
||||||
|
'sftp-private-key': '',
|
||||||
|
'sftp-passphrase': '',
|
||||||
|
'sftp-directory': '',
|
||||||
|
'sftp-root-directory': '',
|
||||||
|
'sftp-server-alive-interval': '',
|
||||||
|
'recording-path': '',
|
||||||
|
'recording-name': '',
|
||||||
|
'recording-exclude-output': '',
|
||||||
|
'recording-exclude-mouse': '',
|
||||||
|
'recording-include-keys': '',
|
||||||
|
'create-recording-path': '',
|
||||||
|
'remote-app': '',
|
||||||
|
'remote-app-dir': '',
|
||||||
|
'remote-app-args': '',
|
||||||
|
'gateway-hostname': '',
|
||||||
|
'gateway-port': '',
|
||||||
|
'gateway-domain': '',
|
||||||
|
'gateway-username': '',
|
||||||
|
'gateway-password': '',
|
||||||
|
'load-balance-info': '',
|
||||||
|
'normalize-clipboard': '',
|
||||||
|
'force-lossless': '',
|
||||||
|
'wol-send-packet': '',
|
||||||
|
'wol-mac-addr': '',
|
||||||
|
'wol-broadcast-addr': '',
|
||||||
|
'wol-udp-port': '',
|
||||||
|
'wol-wait-time': '',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (params.domain) {
|
// Build values array matching the exact order guacd expects
|
||||||
args['domain'] = params.domain;
|
const values = argNames.map((name) => paramMap[name] ?? '');
|
||||||
}
|
|
||||||
|
|
||||||
// Build the connect instruction with opcode + all arg values
|
|
||||||
const values = Object.values(args);
|
|
||||||
return this.encode('connect', ...values);
|
return this.encode('connect', ...values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +186,6 @@ export class GuacamoleService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a Guacamole instruction string back to a string array.
|
* Decode a Guacamole instruction string back to a string array.
|
||||||
* Handles multiple instructions in a single buffer chunk.
|
|
||||||
*/
|
*/
|
||||||
decode(instruction: string): string[] {
|
decode(instruction: string): string[] {
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user