ai-remote 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{src/index.ts → dist/index.d.ts} +0 -1
- package/dist/index.js +0 -1
- package/dist/protocols/index.d.ts +31 -0
- package/dist/protocols/index.js +0 -1
- package/dist/protocols/rdp/buffer.d.ts +38 -0
- package/dist/protocols/rdp/buffer.js +0 -1
- package/dist/protocols/rdp/caps.d.ts +49 -0
- package/dist/protocols/rdp/caps.js +0 -1
- package/dist/protocols/rdp/cert.d.ts +18 -0
- package/dist/protocols/rdp/cert.js +0 -1
- package/dist/protocols/rdp/client.d.ts +56 -0
- package/dist/protocols/rdp/client.js +0 -1
- package/dist/protocols/rdp/cliprdr.d.ts +27 -0
- package/dist/protocols/rdp/cliprdr.js +0 -1
- package/dist/protocols/rdp/credssp.d.ts +34 -0
- package/dist/protocols/rdp/credssp.js +0 -1
- package/dist/protocols/rdp/crypto.d.ts +63 -0
- package/dist/protocols/rdp/crypto.js +0 -1
- package/dist/protocols/rdp/display.d.ts +38 -0
- package/dist/protocols/rdp/display.js +0 -1
- package/dist/protocols/rdp/gcc.d.ts +23 -0
- package/dist/protocols/rdp/gcc.js +0 -1
- package/dist/protocols/rdp/keymap.d.ts +9 -0
- package/dist/protocols/rdp/keymap.js +0 -1
- package/dist/protocols/rdp/mcs.d.ts +40 -0
- package/dist/protocols/rdp/mcs.js +0 -1
- package/dist/protocols/rdp/ntlm.d.ts +61 -0
- package/dist/protocols/rdp/ntlm.js +0 -1
- package/dist/protocols/rdp/pdu.d.ts +155 -0
- package/dist/protocols/rdp/pdu.js +0 -1
- package/dist/protocols/rdp/rail.d.ts +119 -0
- package/dist/protocols/rdp/rail.js +0 -1
- package/dist/protocols/rdp/rle.d.ts +13 -0
- package/dist/protocols/rdp/rle.js +0 -1
- package/dist/protocols/rdp/sec.d.ts +59 -0
- package/dist/protocols/rdp/sec.js +0 -1
- package/dist/protocols/rdp/session.d.ts +62 -0
- package/dist/protocols/rdp/session.js +0 -1
- package/dist/protocols/rdp/tls.d.ts +18 -0
- package/dist/protocols/rdp/tls.js +0 -1
- package/dist/protocols/rdp/vchannel.d.ts +28 -0
- package/dist/protocols/rdp/vchannel.js +0 -1
- package/dist/protocols/rdp/x224.d.ts +40 -0
- package/dist/protocols/rdp/x224.js +0 -1
- package/dist/protocols/ssh/kex.d.ts +97 -0
- package/dist/protocols/ssh/kex.js +0 -1
- package/dist/protocols/ssh/messages.d.ts +50 -0
- package/dist/protocols/ssh/messages.js +0 -1
- package/dist/protocols/ssh/session.d.ts +66 -0
- package/dist/protocols/ssh/session.js +0 -1
- package/dist/protocols/ssh/terminal.d.ts +46 -0
- package/dist/protocols/ssh/terminal.js +0 -1
- package/dist/protocols/ssh/transport.d.ts +62 -0
- package/dist/protocols/ssh/transport.js +0 -1
- package/dist/protocols/ssh/wire.d.ts +52 -0
- package/dist/protocols/ssh/wire.js +0 -1
- package/dist/protocols/types.d.ts +127 -0
- package/dist/protocols/types.js +0 -1
- package/dist/protocols/vnc/input.d.ts +5 -0
- package/dist/protocols/vnc/input.js +0 -1
- package/dist/protocols/vnc/session.d.ts +12 -0
- package/dist/protocols/vnc/session.js +0 -1
- package/dist/shared/connection.d.ts +81 -0
- package/dist/shared/connection.js +0 -1
- package/dist/shared/device.d.ts +26 -0
- package/dist/shared/device.js +0 -1
- package/dist/shared/hosts.d.ts +63 -0
- package/dist/shared/hosts.js +0 -1
- package/dist/shared/icons.d.ts +46 -0
- package/dist/shared/icons.js +0 -1
- package/dist/shared/protocol.d.ts +28 -0
- package/dist/shared/protocol.js +0 -1
- package/dist/shared/signals.d.ts +37 -0
- package/dist/shared/signals.js +0 -1
- package/package.json +10 -11
- package/dist/index.js.map +0 -7
- package/dist/protocols/index.js.map +0 -7
- package/dist/protocols/rdp/buffer.js.map +0 -7
- package/dist/protocols/rdp/caps.js.map +0 -7
- package/dist/protocols/rdp/cert.js.map +0 -7
- package/dist/protocols/rdp/client.js.map +0 -7
- package/dist/protocols/rdp/cliprdr.js.map +0 -7
- package/dist/protocols/rdp/credssp.js.map +0 -7
- package/dist/protocols/rdp/crypto.js.map +0 -7
- package/dist/protocols/rdp/display.js.map +0 -7
- package/dist/protocols/rdp/gcc.js.map +0 -7
- package/dist/protocols/rdp/keymap.js.map +0 -7
- package/dist/protocols/rdp/mcs.js.map +0 -7
- package/dist/protocols/rdp/ntlm.js.map +0 -7
- package/dist/protocols/rdp/pdu.js.map +0 -7
- package/dist/protocols/rdp/rail.js.map +0 -7
- package/dist/protocols/rdp/rle.js.map +0 -7
- package/dist/protocols/rdp/sec.js.map +0 -7
- package/dist/protocols/rdp/session.js.map +0 -7
- package/dist/protocols/rdp/tls.js.map +0 -7
- package/dist/protocols/rdp/vchannel.js.map +0 -7
- package/dist/protocols/rdp/x224.js.map +0 -7
- package/dist/protocols/ssh/kex.js.map +0 -7
- package/dist/protocols/ssh/messages.js.map +0 -7
- package/dist/protocols/ssh/session.js.map +0 -7
- package/dist/protocols/ssh/terminal.js.map +0 -7
- package/dist/protocols/ssh/transport.js.map +0 -7
- package/dist/protocols/ssh/wire.js.map +0 -7
- package/dist/protocols/types.js.map +0 -7
- package/dist/protocols/vnc/input.js.map +0 -7
- package/dist/protocols/vnc/session.js.map +0 -7
- package/dist/shared/connection.js.map +0 -7
- package/dist/shared/device.js.map +0 -7
- package/dist/shared/hosts.js.map +0 -7
- package/dist/shared/icons.js.map +0 -7
- package/dist/shared/protocol.js.map +0 -7
- package/dist/shared/signals.js.map +0 -7
- package/src/cli/cli.ts +0 -462
- package/src/cli/daemon.ts +0 -306
- package/src/cli/framebuffer.ts +0 -115
- package/src/cli/generated/viewer-bundle.ts +0 -5
- package/src/cli/ipc.ts +0 -78
- package/src/cli/native/window.swift +0 -151
- package/src/cli/paths.ts +0 -28
- package/src/cli/png.ts +0 -106
- package/src/cli/session.ts +0 -263
- package/src/cli/shell.ts +0 -184
- package/src/cli/transport.ts +0 -86
- package/src/cli/viewer-client/assets.d.ts +0 -4
- package/src/cli/viewer-client/main.ts +0 -323
- package/src/cli/viewer-page.ts +0 -138
- package/src/cli/viewer.ts +0 -296
- package/src/cli/window.ts +0 -140
- package/src/cli/wsserver.ts +0 -144
- package/src/protocols/index.ts +0 -72
- package/src/protocols/rdp/buffer.ts +0 -204
- package/src/protocols/rdp/caps.ts +0 -304
- package/src/protocols/rdp/cert.ts +0 -190
- package/src/protocols/rdp/client.ts +0 -1371
- package/src/protocols/rdp/cliprdr.ts +0 -344
- package/src/protocols/rdp/credssp.ts +0 -319
- package/src/protocols/rdp/crypto.ts +0 -416
- package/src/protocols/rdp/display.ts +0 -750
- package/src/protocols/rdp/gcc.ts +0 -162
- package/src/protocols/rdp/keymap.ts +0 -68
- package/src/protocols/rdp/mcs.ts +0 -283
- package/src/protocols/rdp/ntlm.ts +0 -380
- package/src/protocols/rdp/pdu.ts +0 -497
- package/src/protocols/rdp/rail.ts +0 -480
- package/src/protocols/rdp/rle.ts +0 -343
- package/src/protocols/rdp/sec.ts +0 -184
- package/src/protocols/rdp/session.ts +0 -393
- package/src/protocols/rdp/tls.ts +0 -452
- package/src/protocols/rdp/vchannel.ts +0 -89
- package/src/protocols/rdp/x224.ts +0 -139
- package/src/protocols/ssh/kex.ts +0 -227
- package/src/protocols/ssh/messages.ts +0 -54
- package/src/protocols/ssh/session.ts +0 -879
- package/src/protocols/ssh/terminal.ts +0 -242
- package/src/protocols/ssh/transport.ts +0 -745
- package/src/protocols/ssh/wire.ts +0 -220
- package/src/protocols/types.ts +0 -146
- package/src/protocols/vnc/input.ts +0 -13
- package/src/protocols/vnc/novnc.d.ts +0 -75
- package/src/protocols/vnc/session.ts +0 -367
- package/src/shared/connection.ts +0 -206
- package/src/shared/device.ts +0 -28
- package/src/shared/hosts.ts +0 -240
- package/src/shared/icons.ts +0 -49
- package/src/shared/protocol.ts +0 -55
- package/src/shared/signals.ts +0 -67
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Icon path data, shared by the server-rendered login page and the React app.
|
|
3
|
+
*
|
|
4
|
+
* Each value is the inside of a 24x24 stroked `<svg>`; the wrapper differs
|
|
5
|
+
* between the two (a string on the Worker, a component in the browser) but the
|
|
6
|
+
* geometry should not.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ICON_PATHS: {
|
|
9
|
+
readonly monitor: '<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>';
|
|
10
|
+
readonly link: '<path d="M9 17H7A5 5 0 0 1 7 7h2M15 7h2a5 5 0 1 1 0 10h-2M8 12h8"/>';
|
|
11
|
+
readonly power: '<path d="M12 2v10"/><path d="M18.4 6.6a9 9 0 1 1-12.8 0"/>';
|
|
12
|
+
readonly maximize: '<path d="M8 3H5a2 2 0 0 0-2 2v3M21 8V5a2 2 0 0 0-2-2h-3M3 16v3a2 2 0 0 0 2 2h3M16 21h3a2 2 0 0 0 2-2v-3"/>';
|
|
13
|
+
readonly minimize: '<path d="M8 3v3a2 2 0 0 1-2 2H3M21 8h-3a2 2 0 0 1-2-2V3M3 16h3a2 2 0 0 1 2 2v3M16 21v-3a2 2 0 0 1 2-2h3"/>';
|
|
14
|
+
readonly keyboard: '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/>';
|
|
15
|
+
readonly clipboard: '<rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>';
|
|
16
|
+
readonly check: '<path d="m5 13 4 4 10-10"/>';
|
|
17
|
+
readonly camera: '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>';
|
|
18
|
+
readonly eye: '<path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12z"/><circle cx="12" cy="12" r="3"/>';
|
|
19
|
+
readonly sliders: '<path d="M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M2 14h4M10 8h4M18 16h4"/>';
|
|
20
|
+
readonly expand: '<path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>';
|
|
21
|
+
readonly shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>';
|
|
22
|
+
readonly chevron: '<path d="m6 9 6 6 6-6"/>';
|
|
23
|
+
readonly back: '<path d="m15 18-6-6 6-6"/>';
|
|
24
|
+
readonly zap: '<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>';
|
|
25
|
+
readonly cube: '<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="m3.3 7 8.7 5 8.7-5M12 22V12"/>';
|
|
26
|
+
readonly logout: '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>';
|
|
27
|
+
readonly clock: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>';
|
|
28
|
+
readonly activity: '<path d="M22 12h-4l-3 9L9 3l-3 9H2"/>';
|
|
29
|
+
readonly transfer: '<path d="m3 16 4 4 4-4M7 20V4M21 8l-4-4-4 4M17 4v16"/>';
|
|
30
|
+
readonly screen: '<rect x="2" y="5" width="20" height="13" rx="2"/><path d="M7 21h10"/>';
|
|
31
|
+
readonly command: '<path d="M15 6a3 3 0 1 1 3 3h-3zm0 0v12m0 0a3 3 0 1 0 3-3h-3zM9 6a3 3 0 1 0-3 3h3zm0 0v12m0 0a3 3 0 1 1-3-3h3z"/>';
|
|
32
|
+
readonly apps: '<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>';
|
|
33
|
+
readonly terminal: '<path d="m4 17 6-6-6-6M12 19h8"/>';
|
|
34
|
+
readonly folder: '<path d="M3 7h6l2 2h10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M3 7V6a2 2 0 0 1 2-2h4l2 3"/>';
|
|
35
|
+
readonly close: '<path d="M18 6 6 18M6 6l12 12"/>';
|
|
36
|
+
readonly sparkles: '<path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3z"/><path d="m19 15 .8 2.2 2.2.8-2.2.8L19 21l-.8-2.2-2.2-.8 2.2-.8z"/>';
|
|
37
|
+
readonly broom: '<path d="M10 10V4a2 2 0 0 1 4 0v6"/><path d="M6 10h12a2 2 0 0 1 2 2v2H4v-2a2 2 0 0 1 2-2Z"/><path d="m5 14-2 7h18l-2-7M9 18l-1 3M15 18l1 3"/>';
|
|
38
|
+
readonly panel: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M14 4v16"/>';
|
|
39
|
+
readonly menu: '<path d="M4 7h16M4 12h16M4 17h16"/>';
|
|
40
|
+
readonly plus: '<path d="M12 5v14M5 12h14"/>';
|
|
41
|
+
readonly star: '<path d="m12 3 2.9 5.9 6.5.9-4.7 4.6 1.1 6.5L12 17.8 6.2 20.9l1.1-6.5L2.6 9.8l6.5-.9L12 3z"/>';
|
|
42
|
+
readonly trash: '<path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>';
|
|
43
|
+
};
|
|
44
|
+
export type IconName = keyof typeof ICON_PATHS;
|
|
45
|
+
/** The tab icon: a monitor in Cloudflare orange. */
|
|
46
|
+
export declare const FAVICON_DATA_URI = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23f38020' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='3' width='20' height='14' rx='2'/%3E%3Cpath d='M8 21h8M12 17v4'/%3E%3C/svg%3E";
|
package/dist/shared/icons.js
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary both halves of the gateway share.
|
|
3
|
+
*
|
|
4
|
+
* The Worker and the browser have to agree on what a "protocol" is, what ports
|
|
5
|
+
* they default to and how an RDP security layer is named, so those live here
|
|
6
|
+
* rather than being re-declared on each side and drifting apart.
|
|
7
|
+
*/
|
|
8
|
+
/** A remote-desktop protocol the session workspace can display. */
|
|
9
|
+
export declare const REMOTE_PROTOCOLS: readonly ['vnc', 'rdp'];
|
|
10
|
+
export type RemoteProtocol = (typeof REMOTE_PROTOCOLS)[number];
|
|
11
|
+
/** Every protocol the gateway relays, including the terminal side panel. */
|
|
12
|
+
export declare const TRANSPORT_PROTOCOLS: readonly ['vnc', 'rdp', 'ssh'];
|
|
13
|
+
export type TransportProtocol = (typeof TRANSPORT_PROTOCOLS)[number];
|
|
14
|
+
/**
|
|
15
|
+
* The RDP security layer. `auto` negotiates with the host, which is right for
|
|
16
|
+
* anything that answers honestly; the rest are for hosts that do not.
|
|
17
|
+
*/
|
|
18
|
+
export declare const RDP_SECURITY_MODES: readonly ['auto', 'nla', 'tls', 'rdp'];
|
|
19
|
+
export type RdpSecurity = (typeof RDP_SECURITY_MODES)[number];
|
|
20
|
+
export declare const DEFAULT_PORTS: Record<TransportProtocol, number>;
|
|
21
|
+
export declare const DEFAULT_SSH_PORT: number;
|
|
22
|
+
export declare function isRemoteProtocol(value: unknown): value is RemoteProtocol;
|
|
23
|
+
export declare function isRdpSecurity(value: unknown): value is RdpSecurity;
|
|
24
|
+
/** Coerce anything to a protocol, falling back to what the caller opens on. */
|
|
25
|
+
export declare function toRemoteProtocol(value: unknown, fallback?: RemoteProtocol): RemoteProtocol;
|
|
26
|
+
export declare function toRdpSecurity(value: unknown, fallback?: RdpSecurity): RdpSecurity;
|
|
27
|
+
/** A port number, or the protocol's default when the value is not one. */
|
|
28
|
+
export declare function toPort(value: unknown, fallback: number): number;
|
package/dist/shared/protocol.js
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control frames the browser and the gateway exchange on the WebSocket that
|
|
3
|
+
* carries a remote-desktop stream.
|
|
4
|
+
*
|
|
5
|
+
* They are plain strings precisely so they can never be confused with the
|
|
6
|
+
* binary protocol payload flowing in either direction, and both sides import
|
|
7
|
+
* these same literals so the two halves cannot drift apart.
|
|
8
|
+
*/
|
|
9
|
+
/** Browser -> gateway: "my WebSocket is open, you may start reading the host". */
|
|
10
|
+
export declare const GATEWAY_READY_SIGNAL = "VNC_GATEWAY_READY_v1";
|
|
11
|
+
/**
|
|
12
|
+
* Browser -> gateway: keep the edge from idling out a silent session.
|
|
13
|
+
* Cloudflare closes a WebSocket with no traffic in either direction for 100s,
|
|
14
|
+
* and an untouched desktop transmits nothing at all.
|
|
15
|
+
*/
|
|
16
|
+
export declare const GATEWAY_KEEPALIVE_SIGNAL = "VNC_GATEWAY_KEEPALIVE_v1";
|
|
17
|
+
/**
|
|
18
|
+
* How often the browser nudges the transport. Well inside the edge's 100s
|
|
19
|
+
* window, so a session that is merely being looked at is never torn down
|
|
20
|
+
* without a close frame -- which is what a bare WebSocket 1006 used to mean.
|
|
21
|
+
*/
|
|
22
|
+
export declare const KEEPALIVE_INTERVAL_MS = 25000;
|
|
23
|
+
export declare const MAX_CLOSE_REASON_BYTES = 123;
|
|
24
|
+
/** Whether a live desktop ended for a transient transport reason worth retrying. */
|
|
25
|
+
export declare function isRetryableDisconnect(diagnostic: {
|
|
26
|
+
close: {
|
|
27
|
+
code: number;
|
|
28
|
+
reason: string;
|
|
29
|
+
} | null;
|
|
30
|
+
established: boolean;
|
|
31
|
+
}): boolean;
|
|
32
|
+
/** WebSocket close reasons are capped at 123 bytes; trim without splitting a code point. */
|
|
33
|
+
export declare function websocketCloseReason(reason: string): string;
|
|
34
|
+
/** 1005/1006/1015 are reserved and cannot be transmitted in a close frame. */
|
|
35
|
+
export declare function sendableCloseCode(code: number): number;
|
|
36
|
+
/** Normalize any WebSocket message payload to bytes. */
|
|
37
|
+
export declare function messageToBytes(data: unknown): Promise<Uint8Array | null>;
|
package/dist/shared/signals.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-remote",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "RDP, VNC and SSH protocol engines that run in Node, a Worker or a browser -- plus `npx ai-remote`, a CLI that drives a machine and shows you what it is doing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,32 +31,31 @@
|
|
|
31
31
|
"agent"
|
|
32
32
|
],
|
|
33
33
|
"main": "./dist/index.js",
|
|
34
|
-
"types": "./
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
35
|
"exports": {
|
|
36
36
|
".": {
|
|
37
|
-
"types": "./
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
38
|
"import": "./dist/index.js",
|
|
39
|
-
"default": "./
|
|
39
|
+
"default": "./dist/index.js"
|
|
40
40
|
},
|
|
41
41
|
"./shared/*": {
|
|
42
|
-
"types": "./
|
|
42
|
+
"types": "./dist/shared/*.d.ts",
|
|
43
43
|
"import": "./dist/shared/*.js",
|
|
44
|
-
"default": "./
|
|
44
|
+
"default": "./dist/shared/*.js"
|
|
45
45
|
},
|
|
46
46
|
"./protocols": {
|
|
47
|
-
"types": "./
|
|
47
|
+
"types": "./dist/protocols/index.d.ts",
|
|
48
48
|
"import": "./dist/protocols/index.js",
|
|
49
|
-
"default": "./
|
|
49
|
+
"default": "./dist/protocols/index.js"
|
|
50
50
|
},
|
|
51
51
|
"./protocols/*": {
|
|
52
|
-
"types": "./
|
|
52
|
+
"types": "./dist/protocols/*.d.ts",
|
|
53
53
|
"import": "./dist/protocols/*.js",
|
|
54
|
-
"default": "./
|
|
54
|
+
"default": "./dist/protocols/*.js"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"files": [
|
|
58
58
|
"dist",
|
|
59
|
-
"src",
|
|
60
59
|
"README.md"
|
|
61
60
|
],
|
|
62
61
|
"scripts": {
|
package/dist/index.js.map
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The vocabulary every host of this stack shares.\n *\n * Deliberately no protocol engines: a Worker that only needs to know what a\n * host roster is should not pull noVNC or an RDP state machine into its\n * bundle. The engines live behind `@remotectl/core/protocols`.\n */\n\nexport * from './shared/connection';\nexport * from './shared/device';\nexport * from './shared/hosts';\nexport * from './shared/icons';\nexport * from './shared/protocol';\nexport * from './shared/signals';\n"],
|
|
5
|
-
"mappings": "AAQA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/protocols/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * The protocol registry the workspace talks to.\n *\n * The UI owns the toolbar, the dialogs, the stats rail and the on-screen\n * keyboard, none of which care whether a session is VNC or RDP. Everything that\n * does differ -- the endpoint, how a client is constructed, the wording of a\n * failure, the labels in the connect dialog -- is looked up here instead of\n * branching at each site.\n */\n\nimport { rdpDriver } from './rdp/session';\nimport { vncDriver } from './vnc/session';\nimport type { DisconnectContext, ProtocolDriver } from './types';\nimport type { RemoteProtocol } from '../shared/protocol';\n\nexport type { ProtocolDriver, RemoteSession, SessionOptions, ScreenGeometry } from './types';\n\nexport const DRIVERS: Record<RemoteProtocol, ProtocolDriver> = {\n vnc: vncDriver,\n rdp: rdpDriver,\n};\n\n/** Unknown names fall back to VNC, which is what the dialog opens on. */\nexport function driverFor(protocol: string | undefined): ProtocolDriver {\n return DRIVERS[protocol as RemoteProtocol] ?? DRIVERS.vnc;\n}\n\n/** What the workspace knows about a session that has just ended. */\nexport interface DisconnectDiagnostic {\n protocol: RemoteProtocol;\n /** The transport close, when one arrived. A 1006 means it did not. */\n close: { code: number; reason: string } | null;\n /** True once the session had reached the desktop. */\n established: boolean;\n}\n\n/**\n * Explain a disconnect in terms someone can act on. The protocol gets first\n * say, then the close codes that mean the same thing either way.\n */\nexport function explainDisconnect(\n diagnostic: DisconnectDiagnostic,\n clean: boolean,\n protocolMessage: string\n): string {\n // A client that reports its own failure states it in the protocol's terms,\n // which is always more specific than anything the transport can say.\n if (protocolMessage) return protocolMessage;\n\n const code = diagnostic.close?.code;\n const reason = diagnostic.close?.reason.trim() ?? '';\n const suffix = code ? ` (WebSocket ${code}${reason ? `: ${reason}` : ''})` : '';\n const context: DisconnectContext = { ...(code === undefined ? {} : { code }), reason, suffix };\n\n const driver = driverFor(diagnostic.protocol);\n const specific = driver.describeDisconnect(context);\n if (specific) return specific;\n\n const name = driver.protocol.toUpperCase();\n if (/tcp (reached )?eof/i.test(reason)) {\n return `The ${name} host accepted the connection and then closed it${suffix}. `\n + 'Check its remote-access permissions and the account name.';\n }\n if (code === 1006) {\n return 'The connection was interrupted before a close reason arrived (WebSocket 1006). '\n + 'This can happen during a deployment or a brief network change. Automatic reconnect '\n + 'did not restore the session; reconnect once, then check Worker live logs if it repeats.';\n }\n if (reason) return `The remote session ended${suffix}.`;\n if (clean) return `The remote server closed the session cleanly${suffix || ' (no reason supplied)'}.`;\n return `The ${name} negotiation failed${suffix || ' before the server supplied a close reason'}.`;\n}\n"],
|
|
5
|
-
"mappings": "AAUA,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAMnB,MAAM,UAAkD;AAAA,EAC7D,KAAK;AAAA,EACL,KAAK;AACP;AAGO,SAAS,UAAU,UAA8C;AACtE,SAAO,QAAQ,QAA0B,KAAK,QAAQ;AACxD;AAeO,SAAS,kBACd,YACA,OACA,iBACQ;AAGR,MAAI,gBAAiB,QAAO;AAE5B,QAAM,OAAO,WAAW,OAAO;AAC/B,QAAM,SAAS,WAAW,OAAO,OAAO,KAAK,KAAK;AAClD,QAAM,SAAS,OAAO,eAAe,IAAI,GAAG,SAAS,KAAK,MAAM,KAAK,EAAE,MAAM;AAC7E,QAAM,UAA6B,EAAE,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK,GAAI,QAAQ,OAAO;AAE7F,QAAM,SAAS,UAAU,WAAW,QAAQ;AAC5C,QAAM,WAAW,OAAO,mBAAmB,OAAO;AAClD,MAAI,SAAU,QAAO;AAErB,QAAM,OAAO,OAAO,SAAS,YAAY;AACzC,MAAI,sBAAsB,KAAK,MAAM,GAAG;AACtC,WAAO,OAAO,IAAI,mDAAmD,MAAM;AAAA,EAE7E;AACA,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,EAGT;AACA,MAAI,OAAQ,QAAO,2BAA2B,MAAM;AACpD,MAAI,MAAO,QAAO,+CAA+C,UAAU,uBAAuB;AAClG,SAAO,OAAO,IAAI,sBAAsB,UAAU,4CAA4C;AAChG;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/protocols/rdp/buffer.ts"],
|
|
4
|
-
"sourcesContent": ["// Binary reader/writer used by every RDP layer. RDP mixes endianness freely --\n// TPKT and MCS are big endian, everything above them is little endian -- so the\n// byte order is part of each call rather than a mode on the object.\n\n/**\n * Plain `Uint8Array` throughout. Pinning the buffer type parameter would be\n * more precise, but every layer above this one passes byte slices around and\n * the variance between `ArrayBuffer` and `ArrayBufferLike` then has to be\n * restated at each boundary for no benefit the protocol code can use.\n */\nfunction allocate(length: number): Uint8Array {\n return new Uint8Array(length);\n}\n\nexport class Writer {\n private bytes: Uint8Array;\n private view: DataView;\n private offset = 0;\n\n constructor(capacity = 512) {\n this.bytes = allocate(capacity);\n this.view = new DataView(this.bytes.buffer);\n }\n\n #ensure(extra: number): void {\n if (this.offset + extra <= this.bytes.length) return;\n let capacity = this.bytes.length * 2;\n while (capacity < this.offset + extra) capacity *= 2;\n const grown = allocate(capacity);\n grown.set(this.bytes.subarray(0, this.offset));\n this.bytes = grown;\n this.view = new DataView(grown.buffer);\n }\n\n u8(value: number): this {\n this.#ensure(1);\n this.view.setUint8(this.offset, value);\n this.offset += 1;\n return this;\n }\n\n u16le(value: number): this {\n this.#ensure(2);\n this.view.setUint16(this.offset, value, true);\n this.offset += 2;\n return this;\n }\n\n u16be(value: number): this {\n this.#ensure(2);\n this.view.setUint16(this.offset, value, false);\n this.offset += 2;\n return this;\n }\n\n u32le(value: number): this {\n this.#ensure(4);\n this.view.setUint32(this.offset, value >>> 0, true);\n this.offset += 4;\n return this;\n }\n\n u32be(value: number): this {\n this.#ensure(4);\n this.view.setUint32(this.offset, value >>> 0, false);\n this.offset += 4;\n return this;\n }\n\n raw(bytes: Uint8Array): this {\n this.#ensure(bytes.length);\n this.bytes.set(bytes, this.offset);\n this.offset += bytes.length;\n return this;\n }\n\n zeros(count: number): this {\n this.#ensure(count);\n this.offset += count;\n return this;\n }\n\n /** UTF-16LE, optionally padded or truncated to a fixed byte width. */\n utf16le(text: string, fixedBytes?: number): this {\n const start = this.offset;\n for (const unit of text) {\n const code = unit.codePointAt(0) ?? 0;\n // Fixed-width fields must keep room for the terminating NUL.\n if (fixedBytes !== undefined && this.offset - start >= fixedBytes - 2) break;\n if (code > 0xffff) {\n // Surrogate pair: both halves are already what UTF-16 needs.\n for (let i = 0; i < unit.length; i++) this.u16le(unit.charCodeAt(i));\n } else {\n this.u16le(code);\n }\n }\n if (fixedBytes !== undefined) {\n this.zeros(fixedBytes - (this.offset - start));\n }\n return this;\n }\n\n /** Overwrite an earlier 16-bit slot, for length fields known only later. */\n patchU16le(position: number, value: number): this {\n this.view.setUint16(position, value, true);\n return this;\n }\n\n patchU16be(position: number, value: number): this {\n this.view.setUint16(position, value, false);\n return this;\n }\n\n get length(): number {\n return this.offset;\n }\n\n take(): Uint8Array {\n return this.bytes.slice(0, this.offset);\n }\n}\n\nexport class Reader {\n private readonly bytes: Uint8Array;\n private readonly view: DataView;\n offset: number;\n\n constructor(bytes: Uint8Array, offset = 0) {\n this.bytes = bytes;\n this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n this.offset = offset;\n }\n\n get remaining(): number {\n return this.bytes.length - this.offset;\n }\n\n #need(count: number): void {\n if (this.remaining < count) {\n throw new Error(`RDP: truncated frame (needed ${count} bytes, ${this.remaining} left)`);\n }\n }\n\n u8(): number {\n this.#need(1);\n return this.view.getUint8(this.offset++);\n }\n\n u16le(): number {\n this.#need(2);\n const value = this.view.getUint16(this.offset, true);\n this.offset += 2;\n return value;\n }\n\n u16be(): number {\n this.#need(2);\n const value = this.view.getUint16(this.offset, false);\n this.offset += 2;\n return value;\n }\n\n u32le(): number {\n this.#need(4);\n const value = this.view.getUint32(this.offset, true);\n this.offset += 4;\n return value;\n }\n\n u32be(): number {\n this.#need(4);\n const value = this.view.getUint32(this.offset, false);\n this.offset += 4;\n return value;\n }\n\n raw(count: number): Uint8Array {\n this.#need(count);\n const slice = this.bytes.subarray(this.offset, this.offset + count);\n this.offset += count;\n return slice;\n }\n\n skip(count: number): this {\n this.#need(count);\n this.offset += count;\n return this;\n }\n\n rest(): Uint8Array {\n return this.bytes.subarray(this.offset);\n }\n}\n\nexport function concatBytes(...parts: readonly Uint8Array[]): Uint8Array {\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = allocate(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n return out;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/protocols/rdp/caps.ts"],
|
|
4
|
-
"sourcesContent": ["// Capability exchange (MS-RDPBCGR 2.2.7).\n//\n// This client deliberately advertises no drawing-order support and no bitmap\n// cache, which makes the host fall back to plain bitmap updates for the whole\n// screen. That is the one output path a from-scratch client can implement\n// correctly, and it is what keeps the rest of this module small.\n\nimport { Reader, Writer, concatBytes } from './buffer';\n\nexport const CAPSET = {\n GENERAL: 1,\n BITMAP: 2,\n ORDER: 3,\n BITMAPCACHE: 4,\n CONTROL: 5,\n ACTIVATION: 7,\n POINTER: 8,\n SHARE: 9,\n COLORCACHE: 10,\n SOUND: 12,\n INPUT: 13,\n FONT: 14,\n BRUSH: 15,\n GLYPHCACHE: 16,\n OFFSCREENCACHE: 17,\n VIRTUALCHANNEL: 20,\n RAIL: 23,\n WINDOW: 24,\n MULTIFRAGMENTUPDATE: 26,\n LARGE_POINTER: 27,\n SURFACE_COMMANDS: 28,\n BITMAP_CODECS: 29,\n};\n\nconst INPUT_FLAG_SCANCODES = 0x0001;\nconst INPUT_FLAG_MOUSEX = 0x0004;\nconst INPUT_FLAG_UNICODE = 0x0010;\n\nconst FASTPATH_OUTPUT_SUPPORTED = 0x0001;\nconst LONG_CREDENTIALS_SUPPORTED = 0x0004;\nconst NEGOTIATE_ORDER_SUPPORT = 0x0002;\nconst ZERO_BOUNDS_DELTA_SUPPORT = 0x0008;\n\nfunction capabilitySet(type: number, body: Uint8Array) {\n const writer = new Writer(body.length + 4);\n writer.u16le(type).u16le(body.length + 4).raw(body);\n return writer.take();\n}\n\nfunction generalCapability() {\n const writer = new Writer(24);\n writer.u16le(1); // osMajorType: Windows\n writer.u16le(3); // osMinorType: Windows NT\n writer.u16le(0x0200); // protocolVersion\n writer.u16le(0); // pad\n writer.u16le(0); // generalCompressionTypes\n // Fast-path output has to be advertised. Windows 8 and later send graphics\n // only on the fast path, so a client that asks for the slow path alone gets a\n // clean session that never paints anything.\n writer.u16le(FASTPATH_OUTPUT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED); // extraFlags\n writer.u16le(0); // updateCapabilityFlag\n writer.u16le(0); // remoteUnshareFlag\n writer.u16le(0); // generalCompressionLevel\n // Both are declarations that the client may SEND these PDUs. The initial\n // full-screen paint depends on it: a host that thinks the client cannot ask\n // for a refresh has no reason to honour the request.\n writer.u8(1); // refreshRectSupport\n writer.u8(1); // suppressOutputSupport\n return capabilitySet(CAPSET.GENERAL, writer.take());\n}\n\nfunction bitmapCapability(width: number, height: number) {\n const writer = new Writer(28);\n writer.u16le(16); // preferredBitsPerPixel\n writer.u16le(1); // receive1BitPerPixel\n writer.u16le(1); // receive4BitsPerPixel\n writer.u16le(1); // receive8BitsPerPixel\n writer.u16le(width);\n writer.u16le(height);\n writer.u16le(0); // pad\n writer.u16le(1); // desktopResizeFlag\n writer.u16le(1); // bitmapCompressionFlag\n writer.u8(0); // highColorFlags\n writer.u8(0); // drawingFlags\n writer.u16le(1); // multipleRectangleSupport\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.BITMAP, writer.take());\n}\n\nfunction orderCapability() {\n const writer = new Writer(88);\n writer.zeros(16); // terminalDescriptor\n writer.u32le(0); // pad\n writer.u16le(1); // desktopSaveXGranularity\n writer.u16le(20); // desktopSaveYGranularity\n writer.u16le(0); // pad\n writer.u16le(1); // maximumOrderLevel\n writer.u16le(0); // numberFonts\n writer.u16le(NEGOTIATE_ORDER_SUPPORT | ZERO_BOUNDS_DELTA_SUPPORT);\n writer.zeros(32); // orderSupport: none\n writer.u16le(0); // textFlags\n writer.u16le(0); // orderSupportExFlags\n writer.u32le(0); // pad\n writer.u32le(230400); // desktopSaveSize\n writer.u16le(0); // pad\n writer.u16le(0); // pad\n writer.u16le(0); // textANSICodePage\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.ORDER, writer.take());\n}\n\n/** Zero entries in every cache: the host must send bitmaps, never cache hits. */\nfunction bitmapCacheCapability() {\n const writer = new Writer(40);\n writer.zeros(24); // pad1..pad6\n writer.u16le(0).u16le(0);\n writer.u16le(0).u16le(0);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.BITMAPCACHE, writer.take());\n}\n\nfunction controlCapability() {\n const writer = new Writer(12);\n writer.u16le(0); // controlFlags\n writer.u16le(0); // remoteDetachFlag\n writer.u16le(2); // controlInterest: never\n writer.u16le(2); // detachInterest: never\n return capabilitySet(CAPSET.CONTROL, writer.take());\n}\n\nfunction activationCapability() {\n const writer = new Writer(12);\n writer.u16le(0).u16le(0).u16le(0).u16le(0);\n return capabilitySet(CAPSET.ACTIVATION, writer.take());\n}\n\nfunction pointerCapability() {\n const writer = new Writer(10);\n writer.u16le(1); // colorPointerFlag\n writer.u16le(20); // colorPointerCacheSize\n writer.u16le(20); // pointerCacheSize\n return capabilitySet(CAPSET.POINTER, writer.take());\n}\n\nfunction shareCapability() {\n const writer = new Writer(8);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.SHARE, writer.take());\n}\n\nfunction colorCacheCapability() {\n const writer = new Writer(8);\n writer.u16le(6).u16le(0);\n return capabilitySet(CAPSET.COLORCACHE, writer.take());\n}\n\nfunction soundCapability() {\n const writer = new Writer(8);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.SOUND, writer.take());\n}\n\nfunction inputCapability(keyboardLayout: number) {\n const writer = new Writer(88);\n writer.u16le(INPUT_FLAG_SCANCODES | INPUT_FLAG_MOUSEX | INPUT_FLAG_UNICODE);\n writer.u16le(0); // pad\n writer.u32le(keyboardLayout);\n writer.u32le(4); // keyboardType: IBM enhanced\n writer.u32le(0); // keyboardSubType\n writer.u32le(12); // keyboardFunctionKey\n writer.zeros(64); // imeFileName\n return capabilitySet(CAPSET.INPUT, writer.take());\n}\n\nfunction fontCapability() {\n const writer = new Writer(8);\n writer.u16le(1).u16le(0);\n return capabilitySet(CAPSET.FONT, writer.take());\n}\n\nfunction brushCapability() {\n const writer = new Writer(8);\n writer.u32le(0); // BRUSH_DEFAULT\n return capabilitySet(CAPSET.BRUSH, writer.take());\n}\n\nfunction glyphCacheCapability() {\n const writer = new Writer(52);\n for (let i = 0; i < 10; i++) writer.u16le(0).u16le(0); // GlyphCache entries\n writer.u32le(0); // FragCache\n writer.u16le(0); // GLYPH_SUPPORT_NONE\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.GLYPHCACHE, writer.take());\n}\n\nfunction offscreenCacheCapability() {\n const writer = new Writer(12);\n writer.u32le(0); // offscreenSupportLevel: none\n writer.u16le(0); // offscreenCacheSize\n writer.u16le(0); // offscreenCacheEntries\n return capabilitySet(CAPSET.OFFSCREENCACHE, writer.take());\n}\n\nfunction remoteProgramsCapability() {\n const writer = new Writer(4);\n writer.u32le(0x00000001); // TS_RAIL_LEVEL_SUPPORTED\n return capabilitySet(CAPSET.RAIL, writer.take());\n}\n\nfunction windowListCapability(iconCaches = 0, iconCacheEntries = 0) {\n const writer = new Writer(7);\n writer.u32le(0x00000001); // TS_WINDOW_LEVEL_SUPPORTED\n writer.u8(iconCaches);\n writer.u16le(iconCacheEntries);\n return capabilitySet(CAPSET.WINDOW, writer.take());\n}\n\nexport function buildClientCapabilities({ width, height, keyboardLayout, remoteApp = false, remoteAppCaps = null }) {\n const sets = [\n generalCapability(),\n bitmapCapability(width, height),\n orderCapability(),\n bitmapCacheCapability(),\n colorCacheCapability(),\n activationCapability(),\n controlCapability(),\n pointerCapability(),\n shareCapability(),\n inputCapability(keyboardLayout),\n brushCapability(),\n glyphCacheCapability(),\n soundCapability(),\n fontCapability(),\n offscreenCacheCapability(),\n ];\n\n if (remoteApp) {\n sets.push(remoteProgramsCapability());\n sets.push(windowListCapability(\n remoteAppCaps?.numIconCaches || 0,\n remoteAppCaps?.numIconCacheEntries || 0\n ));\n }\n\n return { bytes: concatBytes(...sets), count: sets.length };\n}\n\n/**\n * Read the host's Demand Active capabilities. The desktop size it reports wins\n * over anything the client asked for.\n */\nexport function parseServerCapabilities(bytes: Uint8Array) {\n const reader = new Reader(bytes);\n const numberCapabilities = reader.u16le();\n reader.u16le(); // pad\n\n const result = {\n desktopWidth: 0,\n desktopHeight: 0,\n preferredBitsPerPixel: 16,\n refreshRectSupport: false,\n railSupportLevel: 0,\n windowSupportLevel: 0,\n numIconCaches: 0,\n numIconCacheEntries: 0,\n types: [],\n };\n\n for (let i = 0; i < numberCapabilities && reader.remaining >= 4; i++) {\n const type = reader.u16le();\n const length = reader.u16le();\n if (length < 4 || length - 4 > reader.remaining) break;\n const body = new Reader(reader.raw(length - 4));\n result.types.push(type);\n\n if (type === CAPSET.BITMAP && body.remaining >= 20) {\n result.preferredBitsPerPixel = body.u16le();\n body.skip(6); // receive1/4/8BitsPerPixel\n result.desktopWidth = body.u16le();\n result.desktopHeight = body.u16le();\n continue;\n }\n\n if (type === CAPSET.GENERAL && body.remaining >= 20) {\n body.skip(18);\n result.refreshRectSupport = body.u8() !== 0;\n continue;\n }\n\n if (type === CAPSET.RAIL && body.remaining >= 4) {\n result.railSupportLevel = body.u32le();\n continue;\n }\n\n if (type === CAPSET.WINDOW && body.remaining >= 7) {\n result.windowSupportLevel = body.u32le();\n result.numIconCaches = body.u8();\n result.numIconCacheEntries = body.u16le();\n continue;\n }\n }\n\n return result;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/protocols/rdp/cert.ts"],
|
|
4
|
-
"sourcesContent": ["// Server public key extraction for the RDP Standard Security layer.\n//\n// A host either hands out a \"proprietary\" certificate -- little more than a\n// wrapper around an RSA public key -- or an X.509 chain whose leaf holds the\n// key (MS-RDPBCGR 2.2.1.4.3.1). Either way the client random is encrypted with\n// that key, so all we need out of it is a modulus and an exponent.\n//\n// Note the proprietary certificate is signed with a private key Microsoft\n// published, so it authenticates nothing: RDP Standard Security gives\n// confidentiality on the wire, not proof of the host's identity.\n\nimport { Reader } from './buffer';\n\nconst CERT_PROPRIETARY = 1;\nconst CERT_X509 = 2;\nconst RSA1_MAGIC = 0x31415352;\nconst BB_RSA_KEY_BLOB = 0x0006;\n\nconst OID_RSA_ENCRYPTION = [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];\n\n// --- DER --------------------------------------------------------------------\n\nfunction derRead(bytes: Uint8Array, offset: number) {\n let cursor = offset;\n const tag = bytes[cursor++];\n\n let length = bytes[cursor++];\n if (length & 0x80) {\n const byteCount = length & 0x7f;\n length = 0;\n for (let i = 0; i < byteCount; i++) length = (length << 8) | bytes[cursor++];\n }\n\n return {\n tag,\n contentStart: cursor,\n contentEnd: cursor + length,\n next: cursor + length,\n content: bytes.subarray(cursor, cursor + length),\n };\n}\n\nfunction derChildren(bytes: Uint8Array) {\n const children = [];\n let offset = 0;\n while (offset < bytes.length) {\n const node = derRead(bytes, offset);\n children.push(node);\n if (node.next <= offset) break;\n offset = node.next;\n }\n return children;\n}\n\nfunction containsOid(bytes: Uint8Array, oid: string) {\n outer: for (let i = 0; i + oid.length <= bytes.length; i++) {\n for (let j = 0; j < oid.length; j++) {\n if (bytes[i + j] !== oid[j]) continue outer;\n }\n return true;\n }\n return false;\n}\n\n/** Big-endian DER integer -> little-endian bytes with the sign byte removed. */\nfunction derIntegerToLe(content: Uint8Array) {\n let start = 0;\n while (start < content.length - 1 && content[start] === 0x00) start++;\n return content.subarray(start).slice().reverse();\n}\n\n/**\n * Find SubjectPublicKeyInfo in an X.509 certificate and return the RSA key.\n * The structure is walked rather than indexed by position: optional fields in\n * TBSCertificate make fixed offsets wrong for many real certificates.\n */\nfunction rsaKeyFromX509(der: Uint8Array) {\n const certificate = derRead(der, 0);\n const tbsCertificate = derRead(der, certificate.contentStart);\n\n for (const child of derChildren(tbsCertificate.content)) {\n if (child.tag !== 0x30) continue;\n\n const parts = derChildren(child.content);\n if (parts.length < 2) continue;\n if (parts[0].tag !== 0x30 || !containsOid(parts[0].content, OID_RSA_ENCRYPTION)) continue;\n if (parts[1].tag !== 0x03) continue;\n\n // BIT STRING content starts with an \"unused bits\" count.\n const publicKey = derRead(parts[1].content.subarray(1), 0);\n const [modulus, exponent] = derChildren(publicKey.content);\n if (!modulus || !exponent) break;\n\n return {\n modulus: derIntegerToLe(modulus.content),\n exponent: derIntegerToLe(exponent.content),\n source: 'x509',\n };\n }\n\n throw new Error('RDP: no RSA public key found in the host X.509 certificate');\n}\n\nfunction rsaKeyFromProprietary(reader: Reader) {\n reader.u32le(); // dwSigAlgId\n reader.u32le(); // dwKeyAlgId\n\n const publicKeyBlobType = reader.u16le();\n const publicKeyBlobLength = reader.u16le();\n if (publicKeyBlobType !== BB_RSA_KEY_BLOB) {\n throw new Error(`RDP: unsupported public key blob type 0x${publicKeyBlobType.toString(16)}`);\n }\n\n const blob = new Reader(reader.raw(publicKeyBlobLength));\n if (blob.u32le() !== RSA1_MAGIC) {\n throw new Error('RDP: the host RSA public key blob is not in RSA1 format');\n }\n\n const keyLength = blob.u32le();\n blob.u32le(); // bitlen\n blob.u32le(); // datalen\n const exponent = blob.raw(4).slice();\n\n // The blob reserves 8 trailing bytes of padding after the modulus.\n if (keyLength < 9) throw new Error('RDP: the host RSA modulus is too short');\n const modulus = blob.raw(keyLength - 8).slice();\n\n return { modulus, exponent, source: 'proprietary' };\n}\n\n/**\n * Pull the RSA public key out of a DER X.509 certificate, for the TLS layer.\n *\n * CredSSP needs the exact bytes of the certificate's subjectPublicKey BIT\n * STRING contents -- the DER RSAPublicKey -- because that is what both ends\n * hash to bind the authentication to this TLS channel (MS-CSSP 3.1.5).\n */\nexport function parseX509PublicKey(der: Uint8Array) {\n const certificate = derRead(der, 0);\n const tbsCertificate = derRead(der, certificate.contentStart);\n\n for (const child of derChildren(tbsCertificate.content)) {\n if (child.tag !== 0x30) continue;\n\n const parts = derChildren(child.content);\n if (parts.length < 2) continue;\n if (parts[0].tag !== 0x30 || !containsOid(parts[0].content, OID_RSA_ENCRYPTION)) continue;\n if (parts[1].tag !== 0x03) continue;\n\n const subjectPublicKey = parts[1].content.subarray(1); // drop the unused-bit count\n const publicKey = derRead(subjectPublicKey, 0);\n const [modulus, exponent] = derChildren(publicKey.content);\n if (!modulus || !exponent) break;\n\n const stripLeadingZero = (bytes: Uint8Array) => {\n let start = 0;\n while (start < bytes.length - 1 && bytes[start] === 0x00) start++;\n return bytes.subarray(start);\n };\n\n return {\n modulus: stripLeadingZero(modulus.content),\n exponent: stripLeadingZero(exponent.content),\n subjectPublicKey,\n };\n }\n\n throw new Error('TLS: no RSA public key found in the host certificate');\n}\n\n/** Parse a server certificate blob into an RSA public key. */\nexport function parseServerCertificate(certificateBytes: Uint8Array) {\n const reader = new Reader(certificateBytes);\n const version = reader.u32le() & 0x7fffffff;\n\n if (version === CERT_PROPRIETARY) return rsaKeyFromProprietary(reader);\n\n if (version === CERT_X509) {\n const certificateCount = reader.u32le();\n let leaf = null;\n for (let i = 0; i < certificateCount; i++) {\n const length = reader.u32le();\n leaf = reader.raw(length);\n }\n if (!leaf) throw new Error('RDP: the host sent an empty X.509 certificate chain');\n return rsaKeyFromX509(leaf);\n }\n\n throw new Error(`RDP: unsupported server certificate version ${version}`);\n}\n"],
|
|
5
|
-
"mappings": "AAWA,SAAS,cAAc;AAEvB,MAAM,mBAAmB;AACzB,MAAM,YAAY;AAClB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AAExB,MAAM,qBAAqB,CAAC,IAAM,KAAM,IAAM,KAAM,KAAM,IAAM,GAAM,GAAM,CAAI;AAIhF,SAAS,QAAQ,OAAmB,QAAgB;AAClD,MAAI,SAAS;AACb,QAAM,MAAM,MAAM,QAAQ;AAE1B,MAAI,SAAS,MAAM,QAAQ;AAC3B,MAAI,SAAS,KAAM;AACjB,UAAM,YAAY,SAAS;AAC3B,aAAS;AACT,aAAS,IAAI,GAAG,IAAI,WAAW,IAAK,UAAU,UAAU,IAAK,MAAM,QAAQ;AAAA,EAC7E;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc;AAAA,IACd,YAAY,SAAS;AAAA,IACrB,MAAM,SAAS;AAAA,IACf,SAAS,MAAM,SAAS,QAAQ,SAAS,MAAM;AAAA,EACjD;AACF;AAEA,SAAS,YAAY,OAAmB;AACtC,QAAM,WAAW,CAAC;AAClB,MAAI,SAAS;AACb,SAAO,SAAS,MAAM,QAAQ;AAC5B,UAAM,OAAO,QAAQ,OAAO,MAAM;AAClC,aAAS,KAAK,IAAI;AAClB,QAAI,KAAK,QAAQ,OAAQ;AACzB,aAAS,KAAK;AAAA,EAChB;AACA,SAAO;AACT;AAEA,SAAS,YAAY,OAAmB,KAAa;AACnD,QAAO,UAAS,IAAI,GAAG,IAAI,IAAI,UAAU,MAAM,QAAQ,KAAK;AAC1D,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,UAAI,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,EAAG,UAAS;AAAA,IACxC;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,eAAe,SAAqB;AAC3C,MAAI,QAAQ;AACZ,SAAO,QAAQ,QAAQ,SAAS,KAAK,QAAQ,KAAK,MAAM,EAAM;AAC9D,SAAO,QAAQ,SAAS,KAAK,EAAE,MAAM,EAAE,QAAQ;AACjD;AAOA,SAAS,eAAe,KAAiB;AACvC,QAAM,cAAc,QAAQ,KAAK,CAAC;AAClC,QAAM,iBAAiB,QAAQ,KAAK,YAAY,YAAY;AAE5D,aAAW,SAAS,YAAY,eAAe,OAAO,GAAG;AACvD,QAAI,MAAM,QAAQ,GAAM;AAExB,UAAM,QAAQ,YAAY,MAAM,OAAO;AACvC,QAAI,MAAM,SAAS,EAAG;AACtB,QAAI,MAAM,CAAC,EAAE,QAAQ,MAAQ,CAAC,YAAY,MAAM,CAAC,EAAE,SAAS,kBAAkB,EAAG;AACjF,QAAI,MAAM,CAAC,EAAE,QAAQ,EAAM;AAG3B,UAAM,YAAY,QAAQ,MAAM,CAAC,EAAE,QAAQ,SAAS,CAAC,GAAG,CAAC;AACzD,UAAM,CAAC,SAAS,QAAQ,IAAI,YAAY,UAAU,OAAO;AACzD,QAAI,CAAC,WAAW,CAAC,SAAU;AAE3B,WAAO;AAAA,MACL,SAAS,eAAe,QAAQ,OAAO;AAAA,MACvC,UAAU,eAAe,SAAS,OAAO;AAAA,MACzC,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,4DAA4D;AAC9E;AAEA,SAAS,sBAAsB,QAAgB;AAC7C,SAAO,MAAM;AACb,SAAO,MAAM;AAEb,QAAM,oBAAoB,OAAO,MAAM;AACvC,QAAM,sBAAsB,OAAO,MAAM;AACzC,MAAI,sBAAsB,iBAAiB;AACzC,UAAM,IAAI,MAAM,2CAA2C,kBAAkB,SAAS,EAAE,CAAC,EAAE;AAAA,EAC7F;AAEA,QAAM,OAAO,IAAI,OAAO,OAAO,IAAI,mBAAmB,CAAC;AACvD,MAAI,KAAK,MAAM,MAAM,YAAY;AAC/B,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AAEA,QAAM,YAAY,KAAK,MAAM;AAC7B,OAAK,MAAM;AACX,OAAK,MAAM;AACX,QAAM,WAAW,KAAK,IAAI,CAAC,EAAE,MAAM;AAGnC,MAAI,YAAY,EAAG,OAAM,IAAI,MAAM,wCAAwC;AAC3E,QAAM,UAAU,KAAK,IAAI,YAAY,CAAC,EAAE,MAAM;AAE9C,SAAO,EAAE,SAAS,UAAU,QAAQ,cAAc;AACpD;AASO,SAAS,mBAAmB,KAAiB;AAClD,QAAM,cAAc,QAAQ,KAAK,CAAC;AAClC,QAAM,iBAAiB,QAAQ,KAAK,YAAY,YAAY;AAE5D,aAAW,SAAS,YAAY,eAAe,OAAO,GAAG;AACvD,QAAI,MAAM,QAAQ,GAAM;AAExB,UAAM,QAAQ,YAAY,MAAM,OAAO;AACvC,QAAI,MAAM,SAAS,EAAG;AACtB,QAAI,MAAM,CAAC,EAAE,QAAQ,MAAQ,CAAC,YAAY,MAAM,CAAC,EAAE,SAAS,kBAAkB,EAAG;AACjF,QAAI,MAAM,CAAC,EAAE,QAAQ,EAAM;AAE3B,UAAM,mBAAmB,MAAM,CAAC,EAAE,QAAQ,SAAS,CAAC;AACpD,UAAM,YAAY,QAAQ,kBAAkB,CAAC;AAC7C,UAAM,CAAC,SAAS,QAAQ,IAAI,YAAY,UAAU,OAAO;AACzD,QAAI,CAAC,WAAW,CAAC,SAAU;AAE3B,UAAM,mBAAmB,CAAC,UAAsB;AAC9C,UAAI,QAAQ;AACZ,aAAO,QAAQ,MAAM,SAAS,KAAK,MAAM,KAAK,MAAM,EAAM;AAC1D,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B;AAEA,WAAO;AAAA,MACL,SAAS,iBAAiB,QAAQ,OAAO;AAAA,MACzC,UAAU,iBAAiB,SAAS,OAAO;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,sDAAsD;AACxE;AAGO,SAAS,uBAAuB,kBAA8B;AACnE,QAAM,SAAS,IAAI,OAAO,gBAAgB;AAC1C,QAAM,UAAU,OAAO,MAAM,IAAI;AAEjC,MAAI,YAAY,iBAAkB,QAAO,sBAAsB,MAAM;AAErE,MAAI,YAAY,WAAW;AACzB,UAAM,mBAAmB,OAAO,MAAM;AACtC,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,kBAAkB,KAAK;AACzC,YAAM,SAAS,OAAO,MAAM;AAC5B,aAAO,OAAO,IAAI,MAAM;AAAA,IAC1B;AACA,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qDAAqD;AAChF,WAAO,eAAe,IAAI;AAAA,EAC5B;AAEA,QAAM,IAAI,MAAM,+CAA+C,OAAO,EAAE;AAC1E;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/protocols/rdp/client.ts"],
|
|
4
|
-
"sourcesContent": ["// The RDP client connection state machine (MS-RDPBCGR 1.3.1.1).\n//\n// It runs in the browser and talks to the gateway's /rdp-ws relay, which moves\n// bytes to and from TCP port 3389 and performs the TLS upgrade when X.224\n// negotiation asks for one. Everything above the transport -- MCS, the security\n// layer, capability exchange, updates and input -- happens here.\n\nimport { Reader, concatBytes } from './buffer';\nimport { rsaEncrypt } from './crypto';\nimport { NEG_FAILURE, NEG_PROTOCOL, connectionRequest, parseConnectionConfirm, splitFrames } from './x224';\nimport { Tls12Client } from './tls';\nimport { CredSspClient } from './credssp';\nimport {\n MCS_BASE_CHANNEL_ID,\n attachUserRequest,\n channelJoinRequest,\n connectInitial,\n erectDomainRequest,\n parseAttachUserConfirm,\n parseChannelJoinConfirm,\n parseConnectResponse,\n parseDomainPdu,\n sendDataRequest,\n} from './mcs';\nimport { ENCRYPTION_LEVEL, clientData, parseServerData } from './gcc';\nimport { ClipboardChannel } from './cliprdr';\nimport { RailChannel, SHOW_STATE, SYS_COMMAND, isPrimaryWindow, parseRemoteAppWindowOrders } from './rail';\nimport {\n CHANNEL_FLAG_SHOW_PROTOCOL,\n CHANNEL_OPTION,\n ChannelReassembler,\n CHANNEL_CHUNK_LENGTH,\n chunkChannelData,\n} from './vchannel';\nimport { SEC_FLAG, SecurityLayer } from './sec';\nimport { buildClientCapabilities, parseServerCapabilities } from './caps';\nimport {\n CONTROL_ACTION,\n PDU_TYPE,\n PDU_TYPE2,\n POINTER_FLAG,\n UPDATE_TYPE,\n clientInfoPdu,\n confirmActivePdu,\n controlPdu,\n errorInfoMessage,\n extendedMouseEvent,\n fontListPdu,\n inputEventsPdu,\n mouseEvent,\n parseBitmapUpdate,\n parseDemandActive,\n parsePaletteUpdate,\n parsePointerUpdate,\n pointerWheelFlags,\n parseShareControlHeader,\n parseShareDataHeader,\n refreshRectPdu,\n scancodeEvent,\n shareControlPdu,\n shareDataPdu,\n suppressOutputPdu,\n syncEvent,\n synchronizePdu,\n unicodeEvent,\n} from './pdu';\nimport { CTRL_ALT_DEL, lookupScancode } from './keymap';\nimport {\n GATEWAY_KEEPALIVE_SIGNAL,\n GATEWAY_READY_SIGNAL,\n KEEPALIVE_INTERVAL_MS,\n} from '../../shared/signals';\n\n\nconst STATE = {\n IDLE: 'idle',\n NEGOTIATING: 'negotiating',\n TLS_HANDSHAKE: 'tls-handshake',\n CREDSSP: 'credssp',\n MCS_CONNECT: 'mcs-connect',\n ATTACH_USER: 'attach-user',\n JOIN_CHANNELS: 'join-channels',\n SECURE: 'secure-exchange',\n LICENSING: 'licensing',\n CAPABILITIES: 'capabilities',\n CONNECTED: 'connected',\n CLOSED: 'closed',\n};\n\nconst LICENSE_ERROR_ALERT = 0xff;\n\n/**\n * The clipboard channel. ENCRYPT_RDP only matters under the RDP security layer,\n * where the host would otherwise send this channel in the clear.\n *\n * SHOW_PROTOCOL is not optional, and it has to be matched by\n * CHANNEL_FLAG_SHOW_PROTOCOL on every chunk the client sends. Windows silently\n * discards clipboard data that arrives without the pair: the channel opens, the\n * host announces itself, and then nothing the client sends is ever answered.\n *\n * Compression is absent because this client advertises no decompressor.\n */\nconst CLIPBOARD_CHANNEL = {\n name: 'cliprdr',\n options: CHANNEL_OPTION.INITIALIZED | CHANNEL_OPTION.ENCRYPT_RDP | CHANNEL_OPTION.SHOW_PROTOCOL,\n};\n\nconst RAIL_CHANNEL = {\n name: 'rail',\n options: CHANNEL_OPTION.INITIALIZED | CHANNEL_OPTION.ENCRYPT_RDP | CHANNEL_OPTION.SHOW_PROTOCOL,\n};\n\nconst PERF_DISABLE_WALLPAPER = 0x00000001;\nconst PERF_DISABLE_FULLWINDOWDRAG = 0x00000002;\nconst PERF_DISABLE_MENUANIMATIONS = 0x00000004;\nconst REMOTE_APP_CLOSE_GRACE_MS = 1200;\n\n// A window that has just been restored from maximized is not movable until the\n// host has finished the restore, so the move that fits it is sent one beat\n// later. A repeated fit is coalesced over the same delay.\nconst REMOTE_APP_FIT_DELAY_MS = 120;\n\n// How far the host may place the window from what was asked for before the\n// client asks again, and how many times it is worth asking.\nconst FIT_TOLERANCE_PIXELS = 2;\nconst REMOTE_APP_FIT_ATTEMPTS = 4;\nconst REMOTE_APP_FIT_MOVES = 12;\nconst MIN_REMOTE_APP_SIZE = 320;\n\n/**\n * A single RDP connection. Emits:\n * 'ready' { width, height } -- the desktop size is known\n * 'bitmap' { rects } -- decoded rectangles to paint\n * 'palette' Uint8Array -- 8bpp palette update\n * 'pointer' pointer update -- shape, position or visibility\n * 'error' { message } -- fatal, always followed by 'close'\n * 'close' { clean, message }\n */\nexport class RdpClient extends EventTarget {\n constructor(url: string, options = {}) {\n super();\n\n this.url = url;\n this.options = {\n width: 1024,\n height: 768,\n username: '',\n password: '',\n domain: '',\n keyboardLayout: 0x409,\n clientName: 'cf-gateway',\n security: 'auto',\n /** Returns a WebSocket-shaped transport for `url`; default is a real one. */\n openTransport: null,\n clipboard: true,\n remoteApp: null,\n performance: PERF_DISABLE_WALLPAPER | PERF_DISABLE_FULLWINDOWDRAG | PERF_DISABLE_MENUANIMATIONS,\n ...options,\n };\n\n this.state = STATE.IDLE;\n this.legacyRetryUsed = false;\n this.desktopWidth = this.options.width;\n this.desktopHeight = this.options.height;\n this.palette = null;\n\n this.userId = MCS_BASE_CHANNEL_ID;\n this.ioChannelId = 1003;\n this.serverChannelId = 0x03ea;\n this.shareId = 0;\n this.channelsToJoin = [];\n this.requestedChannels = [];\n /** Server-assigned virtual channel ids, keyed by the name we asked for. */\n this.channelNamesById = new Map();\n this.channelReassemblers = new Map();\n this.clipboard = null;\n this.rail = null;\n /** Live RemoteApp windows and their last reported geometry, by window id. */\n this.remoteAppWindows = new Map();\n this.remoteAppHadWindow = false;\n this.remoteAppCloseTimer = null;\n this.remoteAppFit = null;\n this.remoteAppFitTimer = null;\n this.clientRandom = null;\n this.serverData = null;\n this.fastPathFragments = [];\n\n this.receiveBuffer = new Uint8Array(0);\n this.keepaliveTimer = null;\n this.closeReported = false;\n\n // TLS and CredSSP run here rather than at the edge: the gateway's runtime\n // hides the host certificate that CredSSP must bind to, and validates it\n // against public CAs, which no self-signed RDP host can satisfy.\n this.tls = null;\n this.credssp = null;\n this.inboundQueue = Promise.resolve();\n\n // \"auto\" offers both modern layers and lets the host choose, which is what\n // the X.224 negotiation is for. HYBRID_EX is left out on purpose: a host\n // offered it will pick it, and it needs a round trip this client does not\n // implement.\n this.requestedProtocol = {\n auto: NEG_PROTOCOL.SSL | NEG_PROTOCOL.HYBRID,\n nla: NEG_PROTOCOL.SSL | NEG_PROTOCOL.HYBRID,\n tls: NEG_PROTOCOL.SSL,\n rdp: NEG_PROTOCOL.RDP,\n }[this.options.security] ?? (NEG_PROTOCOL.SSL | NEG_PROTOCOL.HYBRID);\n\n this.#openTransport();\n }\n\n /**\n * Open the transport and reset everything that belongs to one connection.\n * Falling back to another security layer needs a second socket, because a\n * host that refuses the negotiation closes the first.\n */\n #openTransport() {\n this.receiveBuffer = new Uint8Array(0);\n this.inboundQueue = Promise.resolve();\n this.tls = null;\n this.credssp = null;\n this.security = new SecurityLayer();\n this.replacingTransport = false;\n this.channelNamesById = new Map();\n this.channelReassemblers = new Map();\n this.clipboard = null;\n this.rail = null;\n this.remoteAppWindows.clear();\n this.remoteAppHadWindow = false;\n clearTimeout(this.remoteAppCloseTimer);\n clearTimeout(this.remoteAppFitTimer);\n this.remoteAppFitTimer = null;\n this.remoteAppCloseTimer = null;\n\n // The transport is injected so this client can run anywhere. In a browser\n // it is a WebSocket to the gateway's relay; in the CLI it is a TCP socket\n // straight at the host. Everything above it -- X.224, TLS, CredSSP, MCS --\n // is the same either way, which is the whole reason it is a seam.\n this.socket = this.options.openTransport\n ? this.options.openTransport(this.url)\n : new WebSocket(this.url);\n this.socket.binaryType = 'arraybuffer';\n this.socket.addEventListener('open', () => this.#onOpen());\n this.socket.addEventListener('message', (event) => this.#onMessage(event));\n this.socket.addEventListener('close', (event) => this.#onSocketClose(event));\n this.socket.addEventListener('error', () => {\n // The browser never exposes the underlying error; the close event that\n // follows carries the gateway's reason.\n console.warn('[RDP] WebSocket transport error', { target: this.url });\n });\n }\n\n /** Reconnect asking for the only security layer this host will accept. */\n #retryWithSecurityLayer(security) {\n console.info('[RDP] Reconnecting with a different security layer', { security });\n this.legacyRetryUsed = true;\n this.options.security = security;\n this.requestedProtocol = security === 'rdp' ? NEG_PROTOCOL.RDP : NEG_PROTOCOL.SSL;\n this.state = STATE.IDLE;\n\n // The old socket's close must not be reported as the session ending.\n this.replacingTransport = true;\n clearInterval(this.keepaliveTimer);\n try { this.socket.close(1000, 'Retrying with another security layer'); } catch {}\n\n this.#openTransport();\n }\n\n // --- transport ------------------------------------------------------------\n\n #onOpen() {\n // The relay holds off reading the host until the browser says it is ready,\n // so the negotiation response cannot be lost.\n this.socket.send(GATEWAY_READY_SIGNAL);\n\n this.state = STATE.NEGOTIATING;\n this.#send(connectionRequest(this.requestedProtocol));\n\n this.keepaliveTimer = setInterval(() => {\n if (this.socket.readyState !== WebSocket.OPEN) return;\n\n // Keeps the browser-to-gateway WebSocket from idling out at the edge.\n // The gateway absorbs this frame; it never reaches the host.\n this.socket.send(GATEWAY_KEEPALIVE_SIGNAL);\n\n // Which is why the session also needs traffic of its own. A desktop\n // nobody is touching sends nothing for minutes at a time, and the TCP\n // connection carrying it -- through the tunnel, through the VPC binding\n // -- is dropped as idle long before the user has finished reading the\n // screen. A sync event is what mstsc sends for the same reason: it\n // restates the toggle-key state, changes nothing, and is traffic.\n if (this.state === STATE.CONNECTED) {\n try {\n this.#sendInput([syncEvent(this.toggleFlags || 0)]);\n } catch {\n // A keepalive is not worth breaking a working session over.\n }\n }\n }, KEEPALIVE_INTERVAL_MS);\n }\n\n /** RDP-level send: once a secure channel exists everything rides inside it. */\n #send(bytes: Uint8Array) {\n if (this.tls && this.tls.established) {\n this.tls.write(bytes);\n return;\n }\n this.#sendRaw(bytes);\n }\n\n #sendRaw(bytes: Uint8Array) {\n if (this.socket.readyState !== WebSocket.OPEN) return;\n this.socket.send(bytes);\n }\n\n #onSocketClose(event) {\n clearInterval(this.keepaliveTimer);\n clearTimeout(this.remoteAppCloseTimer);\n clearTimeout(this.remoteAppFitTimer);\n this.remoteAppFitTimer = null;\n if (this.replacingTransport) return;\n if (this.state === STATE.CLOSED && this.closeReported) return;\n\n const wasConnected = this.state === STATE.CONNECTED;\n this.state = STATE.CLOSED;\n this.closeReported = true;\n\n this.dispatchEvent(new CustomEvent('close', {\n detail: {\n clean: event.wasClean && wasConnected,\n code: event.code,\n message: event.reason || '',\n phase: wasConnected ? 'session' : 'handshake',\n },\n }));\n }\n\n #fail(error: unknown) {\n if (this.state === STATE.CLOSED) return;\n const message = error instanceof Error ? error.message : String(error);\n console.error('[RDP] Protocol failure', { state: this.state, message });\n\n this.dispatchEvent(new CustomEvent('error', { detail: { message, phase: this.state } }));\n this.state = STATE.CLOSED;\n this.closeReported = true;\n clearInterval(this.keepaliveTimer);\n clearTimeout(this.remoteAppCloseTimer);\n clearTimeout(this.remoteAppFitTimer);\n this.remoteAppFitTimer = null;\n\n try { this.socket.close(4000, message.slice(0, 100)); } catch {}\n this.dispatchEvent(new CustomEvent('close', {\n detail: { clean: false, code: 4000, message, phase: 'protocol' },\n }));\n }\n\n disconnect() {\n if (this.state === STATE.CLOSED) return;\n this.state = STATE.CLOSED;\n this.closeReported = true;\n clearInterval(this.keepaliveTimer);\n clearTimeout(this.remoteAppCloseTimer);\n clearTimeout(this.remoteAppFitTimer);\n this.remoteAppFitTimer = null;\n try { this.socket.close(1000, 'Client disconnected'); } catch {}\n }\n\n /**\n * Transport bytes are processed strictly in order. Nothing awaited in here\n * may itself wait for a later message, or the pipeline would deadlock -- the\n * TLS and CredSSP startup is therefore detached and driven by callbacks.\n */\n #onMessage(event) {\n if (this.state === STATE.CLOSED) return;\n\n this.inboundQueue = this.inboundQueue\n .then(async () => {\n if (this.state === STATE.CLOSED) return;\n\n const chunk = typeof event.data === 'string'\n ? new TextEncoder().encode(event.data)\n : new Uint8Array(event.data);\n\n // Before the secure channel exists these are RDP frames; afterwards\n // they are TLS records carrying them.\n if (this.tls) await this.tls.receive(chunk);\n else await this.#ingest(chunk);\n })\n .catch((error: unknown) => this.#fail(error));\n }\n\n /** Plaintext coming out of the secure channel. */\n #onSecureData(bytes: Uint8Array) {\n if (this.state === STATE.CREDSSP && this.credssp) {\n this.credssp.receive(bytes);\n return;\n }\n return this.#ingest(bytes);\n }\n\n async #ingest(chunk: Uint8Array) {\n this.receiveBuffer = this.receiveBuffer.length === 0\n ? chunk\n : concatBytes(this.receiveBuffer, chunk);\n\n const { frames, rest } = splitFrames(this.receiveBuffer);\n this.receiveBuffer = rest.length === this.receiveBuffer.length ? this.receiveBuffer : rest.slice();\n\n for (const { fastPath, frame } of frames) {\n if (this.state === STATE.CLOSED) return;\n if (fastPath) this.#handleFastPath(frame);\n else await this.#handleX224(frame);\n }\n }\n\n // --- connection sequence --------------------------------------------------\n\n async #handleX224(frame: Uint8Array) {\n if (this.state === STATE.NEGOTIATING) {\n let selectedProtocol;\n try {\n ({ selectedProtocol } = parseConnectionConfirm(frame));\n } catch (error) {\n // A host offering only the legacy RDP security layer refuses TLS\n // outright. Negotiating automatically, that is an answer rather than a\n // failure: reconnect asking for the layer it does have.\n if (\n this.options.security === 'auto' &&\n !this.legacyRetryUsed &&\n error.negotiationFailure === NEG_FAILURE.SSL_NOT_ALLOWED\n ) {\n this.#retryWithSecurityLayer('rdp');\n return;\n }\n throw error;\n }\n\n this.selectedProtocol = selectedProtocol;\n console.info('[RDP] X.224 negotiation complete', {\n requested: this.requestedProtocol,\n selected: selectedProtocol,\n layer: selectedProtocol & NEG_PROTOCOL.HYBRID\n ? 'NLA / CredSSP'\n : selectedProtocol & NEG_PROTOCOL.SSL ? 'TLS' : 'RDP standard',\n });\n\n if (selectedProtocol & NEG_PROTOCOL.HYBRID_EX) {\n // CredSSP-with-early-user-auth adds a round trip this client does not\n // implement; asking for HYBRID alone avoids it.\n throw new Error('This host selected CredSSP with early user authorization, which this client does not implement.');\n }\n\n // A host that chose TLS or NLA gets its own TLS session, established\n // here so the certificate is visible and self-signed is acceptable.\n if (selectedProtocol & (NEG_PROTOCOL.SSL | NEG_PROTOCOL.HYBRID)) {\n void this.#startSecureChannel(selectedProtocol);\n return;\n }\n\n this.#startMcsConnect(selectedProtocol);\n return;\n }\n\n if (this.state === STATE.MCS_CONNECT) {\n this.serverData = parseServerData(parseConnectResponse(frame));\n console.info('[RDP] MCS connect response', {\n encryptionMethod: this.serverData.encryptionMethod,\n encryptionLevel: this.serverData.encryptionLevel,\n ioChannelId: this.serverData.ioChannelId,\n certificate: this.serverData.publicKey?.source || 'none',\n virtualChannels: this.serverData.channelIds,\n });\n\n this.ioChannelId = this.serverData.ioChannelId;\n\n // The host answers with one id per requested channel, in the order asked.\n this.channelNamesById = new Map();\n this.requestedChannels.forEach((channel, index) => {\n const channelId = this.serverData.channelIds[index];\n if (channelId !== undefined) this.channelNamesById.set(channelId, channel.name);\n });\n\n this.security.configure(\n this.serverData.encryptionMethod,\n this.serverData.encryptionLevel,\n // The client random is only needed when the RDP security layer is live.\n this.serverData.encryptionMethod ? this.#makeClientRandom() : new Uint8Array(32),\n this.serverData.serverRandom || new Uint8Array(32)\n );\n\n this.state = STATE.ATTACH_USER;\n this.#send(erectDomainRequest());\n this.#send(attachUserRequest());\n return;\n }\n\n if (this.state === STATE.ATTACH_USER) {\n ({ userId: this.userId } = parseAttachUserConfirm(frame));\n this.channelsToJoin = [this.userId, this.ioChannelId, ...this.serverData.channelIds];\n this.joinedChannels = 0;\n this.state = STATE.JOIN_CHANNELS;\n this.#send(channelJoinRequest(this.userId, this.channelsToJoin[0]));\n return;\n }\n\n if (this.state === STATE.JOIN_CHANNELS) {\n parseChannelJoinConfirm(frame);\n this.joinedChannels += 1;\n\n if (this.joinedChannels < this.channelsToJoin.length) {\n this.#send(channelJoinRequest(this.userId, this.channelsToJoin[this.joinedChannels]));\n return;\n }\n\n this.#startSecureSession();\n return;\n }\n\n // Past the connect sequence everything arrives as a send-data indication.\n const pdu = parseDomainPdu(frame);\n if (pdu.type === 'disconnect') {\n this.#fail(new Error(pdu.message));\n return;\n }\n if (pdu.type !== 'send-data') return;\n\n // Virtual channels are routed by id, before the security header heuristic\n // below: a channel PDU's own length field can look exactly like one.\n const channelName = this.channelNamesById.get(pdu.channelId);\n if (channelName) {\n const body = this.security.headerPresent\n ? this.security.unwrap(pdu.payload).payload\n : pdu.payload;\n this.#handleChannelData(channelName, body);\n return;\n }\n\n const { flags, payload } = this.#unwrapSecurity(pdu.payload);\n if (payload.length === 0) return;\n\n if (flags & SEC_FLAG.LICENSE_PKT) {\n this.#handleLicensing(payload);\n return;\n }\n\n this.#handleSharePdu(payload);\n }\n\n /**\n * Basic security headers are mandatory on licensing PDUs even when the host\n * reported no RDP-level encryption, so during that phase the header has to be\n * recognised rather than assumed. A share control PDU never has a zero pduType,\n * which is what tells the two apart.\n */\n #unwrapSecurity(bytes: Uint8Array) {\n if (this.security.headerPresent) return this.security.unwrap(bytes);\n\n const KNOWN_FLAGS =\n SEC_FLAG.EXCHANGE_PKT | SEC_FLAG.ENCRYPT | SEC_FLAG.INFO_PKT | SEC_FLAG.LICENSE_PKT |\n SEC_FLAG.LICENSE_ENCRYPT_CS | SEC_FLAG.REDIRECTION_PKT | SEC_FLAG.AUTODETECT_REQ |\n SEC_FLAG.HEARTBEAT;\n\n if (bytes.length >= 4) {\n const flags = bytes[0] | (bytes[1] << 8);\n const flagsHi = bytes[2] | (bytes[3] << 8);\n if (flagsHi === 0 && (flags & KNOWN_FLAGS) !== 0) {\n return this.security.unwrap(bytes, true);\n }\n }\n\n return { flags: 0, payload: bytes };\n }\n\n /**\n * Establish TLS and, when the host asked for NLA, authenticate with CredSSP\n * before the RDP connection sequence starts. Detached from the inbound\n * pipeline on purpose: it waits for messages that the pipeline delivers.\n */\n async #startSecureChannel(selectedProtocol: number) {\n try {\n this.state = STATE.TLS_HANDSHAKE;\n this.tls = new Tls12Client({\n send: (bytes: Uint8Array) => this.#sendRaw(bytes),\n onData: (bytes: Uint8Array) => this.#onSecureData(bytes),\n onError: (error: unknown) => this.#fail(error),\n });\n\n const session = await this.tls.start();\n console.info('[RDP] TLS established', {\n cipherSuite: session.cipherSuite,\n certificates: session.certificates.length,\n modulusBits: session.publicKey.modulus.length * 8,\n });\n\n if (selectedProtocol & NEG_PROTOCOL.HYBRID) {\n this.state = STATE.CREDSSP;\n this.credssp = new CredSspClient({\n username: this.options.username,\n domain: this.options.domain,\n password: this.options.password,\n serverPublicKey: session.publicKey.subjectPublicKey,\n send: (bytes: Uint8Array) => this.tls.write(bytes),\n log: (step: number, detail) => console.info(`[RDP] ${step}`, detail),\n });\n\n if (!this.options.password) {\n throw new Error('This host authenticates before the desktop appears (NLA), so it needs a username and password.');\n }\n\n const outcome = await this.credssp.authenticate();\n console.info('[RDP] Network Level Authentication succeeded', outcome);\n this.credssp = null;\n }\n\n this.#startMcsConnect(selectedProtocol);\n } catch (error) {\n this.#fail(error);\n }\n }\n\n #startMcsConnect(selectedProtocol: number) {\n this.state = STATE.MCS_CONNECT;\n this.requestedChannels = [];\n if (this.options.clipboard) this.requestedChannels.push(CLIPBOARD_CHANNEL);\n if (this.options.remoteApp?.program) this.requestedChannels.push(RAIL_CHANNEL);\n this.#send(connectInitial(clientData({\n width: this.options.width,\n height: this.options.height,\n clientName: this.options.clientName,\n keyboardLayout: this.options.keyboardLayout,\n selectedProtocol,\n // Only the bare RDP security layer needs RDP's own encryption; with TLS\n // underneath, the host reports no encryption method at all.\n encryptionMethods: selectedProtocol === NEG_PROTOCOL.RDP ? 0x0000000b : 0,\n channels: this.requestedChannels,\n })));\n }\n\n #makeClientRandom() {\n this.clientRandom = new Uint8Array(32);\n crypto.getRandomValues(this.clientRandom);\n return this.clientRandom;\n }\n\n /**\n * Send the security exchange (when the RDP security layer is in use) followed\n * by the client info PDU that carries the credentials.\n */\n #startSecureSession() {\n if (this.security.encryptionEnabled) {\n const key = this.serverData.publicKey;\n if (!key) throw new Error('RDP: the host asked for encryption but sent no public key');\n\n const encryptedRandom = rsaEncrypt(this.clientRandom, key.modulus, key.exponent);\n this.#sendOnIoChannel(SecurityLayer.securityExchange(encryptedRandom));\n }\n\n const info = clientInfoPdu({\n domain: this.options.domain,\n username: this.options.username,\n password: this.options.password,\n performanceFlags: this.options.performance,\n autoLogon: Boolean(this.options.password),\n remoteApp: Boolean(this.options.remoteApp?.program),\n });\n\n this.state = STATE.LICENSING;\n this.#sendOnIoChannel(this.security.wrap(info, SEC_FLAG.INFO_PKT));\n }\n\n #sendOnIoChannel(payload: Uint8Array) {\n this.#send(sendDataRequest(this.userId, this.ioChannelId, payload));\n }\n\n #handleLicensing(payload: Uint8Array) {\n if (payload.length < 4) return;\n const messageType = payload[0];\n\n if (messageType === LICENSE_ERROR_ALERT) {\n // The expected answer for a host that needs no per-device licence.\n console.info('[RDP] Licensing finished with an error alert (the normal path)');\n this.state = STATE.CAPABILITIES;\n return;\n }\n\n throw new Error(\n 'This host requires Remote Desktop licensing negotiation (per-device CAL), which this client does not implement.'\n );\n }\n\n // --- share layer ----------------------------------------------------------\n\n #handleSharePdu(payload: Uint8Array) {\n const control = parseShareControlHeader(payload);\n if (control.pduSource) this.serverChannelId = control.pduSource;\n\n if (control.pduType === PDU_TYPE.DEMAND_ACTIVE) {\n this.#handleDemandActive(control.payload);\n return;\n }\n\n if (control.pduType === PDU_TYPE.DEACTIVATE_ALL) {\n // The host is about to re-run capability exchange, typically because the\n // desktop was resized or the session was reconnected.\n console.info('[RDP] Host deactivated the share; waiting for a new demand active');\n this.state = STATE.CAPABILITIES;\n return;\n }\n\n if (control.pduType === PDU_TYPE.SERVER_REDIRECT) {\n throw new Error('This host redirected the session to another server, which this client cannot follow.');\n }\n\n if (control.pduType !== PDU_TYPE.DATA) return;\n\n const data = parseShareDataHeader(control.payload);\n if (data.compressedType & 0x20) {\n throw new Error('RDP: the host compressed a PDU although this client advertised no compression support');\n }\n\n this.#handleDataPdu(data);\n }\n\n #handleDemandActive(payload: Uint8Array) {\n const { shareId, capabilities } = parseDemandActive(payload);\n this.shareId = shareId;\n\n const serverCaps = parseServerCapabilities(capabilities);\n if (this.options.remoteApp?.program && (!(serverCaps.railSupportLevel & 1) || !serverCaps.windowSupportLevel)) {\n throw new Error('This RDP host did not accept RemoteApp mode. Publish the application on the host or connect without a RemoteApp program.');\n }\n if (serverCaps.desktopWidth && serverCaps.desktopHeight) {\n const resized = this.state === STATE.CONNECTED &&\n (serverCaps.desktopWidth !== this.desktopWidth || serverCaps.desktopHeight !== this.desktopHeight);\n this.desktopWidth = serverCaps.desktopWidth;\n this.desktopHeight = serverCaps.desktopHeight;\n\n // A host may re-run capability exchange with a different desktop size.\n // The canvas has to follow it, or every later update lands at the wrong\n // coordinates on a framebuffer of the old size.\n if (resized) {\n console.info('[RDP] Host resized the desktop', {\n desktop: `${this.desktopWidth}x${this.desktopHeight}`,\n });\n this.dispatchEvent(new CustomEvent('resize', {\n detail: { width: this.desktopWidth, height: this.desktopHeight },\n }));\n }\n }\n this.refreshRectSupported = serverCaps.refreshRectSupport;\n\n console.info('[RDP] Demand active', {\n shareId,\n desktop: `${this.desktopWidth}x${this.desktopHeight}`,\n bitsPerPixel: serverCaps.preferredBitsPerPixel,\n refreshRectSupport: serverCaps.refreshRectSupport,\n remoteApp: Boolean(this.options.remoteApp?.program),\n });\n\n const { bytes, count } = buildClientCapabilities({\n width: this.desktopWidth,\n height: this.desktopHeight,\n keyboardLayout: this.options.keyboardLayout,\n remoteApp: Boolean(this.options.remoteApp?.program),\n remoteAppCaps: serverCaps,\n });\n\n this.#sendShareControl(\n PDU_TYPE.CONFIRM_ACTIVE,\n confirmActivePdu(this.shareId, bytes, count)\n );\n\n // The host expects this exact sequence before it will send any output.\n this.#sendShareData(PDU_TYPE2.SYNCHRONIZE, synchronizePdu(this.serverChannelId));\n this.#sendShareData(PDU_TYPE2.CONTROL, controlPdu(CONTROL_ACTION.COOPERATE));\n this.#sendShareData(PDU_TYPE2.CONTROL, controlPdu(CONTROL_ACTION.REQUEST_CONTROL));\n this.#sendShareData(PDU_TYPE2.FONT_LIST, fontListPdu());\n }\n\n #handleDataPdu(data: Uint8Array) {\n switch (data.pduType2) {\n case PDU_TYPE2.UPDATE:\n this.#handleUpdate(new Reader(data.payload));\n return;\n\n case PDU_TYPE2.POINTER:\n this.dispatchEvent(new CustomEvent('pointer', { detail: parsePointerUpdate(new Reader(data.payload)) }));\n return;\n\n case PDU_TYPE2.FONT_MAP:\n this.#markConnected();\n return;\n\n case PDU_TYPE2.SET_ERROR_INFO: {\n const errorInfo = new Reader(data.payload).u32le();\n if (errorInfo === 0) return;\n this.#fail(new Error(errorInfoMessage(errorInfo)));\n return;\n }\n\n case PDU_TYPE2.SAVE_SESSION_INFO:\n console.info('[RDP] Host reported a completed sign-in');\n return;\n\n case PDU_TYPE2.SHUTDOWN_DENIED:\n console.info('[RDP] Host denied the shutdown request');\n return;\n\n case PDU_TYPE2.CONTROL:\n case PDU_TYPE2.SYNCHRONIZE:\n case PDU_TYPE2.PLAY_SOUND:\n case PDU_TYPE2.MONITOR_LAYOUT:\n return;\n\n default:\n return;\n }\n }\n\n #handleUpdate(reader: Reader) {\n const updateType = reader.u16le();\n\n if (!this.firstUpdateLogged) {\n this.firstUpdateLogged = true;\n console.info('[RDP] First host update received', { updateType });\n }\n\n if (updateType === UPDATE_TYPE.BITMAP) {\n const rects = parseBitmapUpdate(reader);\n if (rects.length > 0) {\n this.dispatchEvent(new CustomEvent('bitmap', { detail: { rects, palette: this.palette } }));\n }\n return;\n }\n\n if (updateType === UPDATE_TYPE.PALETTE) {\n this.palette = parsePaletteUpdate(reader);\n this.dispatchEvent(new CustomEvent('palette', { detail: this.palette }));\n return;\n }\n\n if (updateType === UPDATE_TYPE.ORDERS) {\n if (this.options.remoteApp?.program) {\n this.#handleRemoteAppWindowEvents(parseRemoteAppWindowOrders(reader));\n } else {\n // No primary order support was advertised. Windowing Alternate\n // Secondary orders are the only exception, and only in RemoteApp mode.\n console.warn('[RDP] Ignoring a drawing-order update the client did not advertise');\n }\n return;\n }\n }\n\n #handleRemoteAppWindowEvents(events) {\n for (const event of events) {\n if (event.type === 'deleted') {\n this.remoteAppWindows.delete(event.windowId);\n this.dispatchEvent(new CustomEvent('remoteappwindow', {\n detail: { ...event, count: this.remoteAppWindows.size },\n }));\n continue;\n }\n\n if (event.type === 'new') {\n clearTimeout(this.remoteAppCloseTimer);\n this.remoteAppCloseTimer = null;\n this.remoteAppHadWindow = true;\n } else if (!this.remoteAppWindows.has(event.windowId)) {\n // An update for a window whose creation order was never seen. Tracking\n // it anyway keeps the session alive and the geometry usable.\n this.remoteAppHadWindow = true;\n }\n\n // Only the changed fields are present in an update, so each one is\n // folded into what the window was already known to be.\n const known = this.remoteAppWindows.get(event.windowId) || { windowId: event.windowId };\n const tracked = { ...known, ...event, type: undefined };\n this.remoteAppWindows.set(event.windowId, tracked);\n\n this.dispatchEvent(new CustomEvent('remoteappwindow', {\n detail: { ...event, window: tracked, count: this.remoteAppWindows.size },\n }));\n\n // The host may place a new window anywhere, and a user-driven maximize\n // sizes it to the work area rather than to the browser. Either way the\n // pending fit is what the browser window currently is, so re-apply it.\n if (this.remoteAppFit && this.#isFittedWindow(tracked) && !this.#matchesFit(tracked)) {\n this.#scheduleRemoteAppFit();\n }\n }\n\n this.#publishRemoteAppViewport();\n\n if (!this.remoteAppHadWindow || this.remoteAppWindows.size > 0 || this.remoteAppCloseTimer) return;\n\n // Chrome and Electron apps can replace a splash/initial window during\n // startup. Give a replacement window a short chance to arrive, then end\n // the otherwise blank RDP connection when the final window stays gone.\n this.remoteAppCloseTimer = setTimeout(() => {\n this.remoteAppCloseTimer = null;\n if (this.state !== STATE.CONNECTED || this.remoteAppWindows.size > 0) return;\n console.info('[RDP] Last RemoteApp window closed; ending the session');\n this.dispatchEvent(new CustomEvent('remoteappclosed', {\n detail: { application: this.options.remoteApp },\n }));\n }, REMOTE_APP_CLOSE_GRACE_MS);\n }\n\n /**\n * The window the session exists to show: the largest top-level application\n * window. Menus, tooltips and drag images are RAIL windows too, and fitting\n * one of those to the browser instead of the application would be worse than\n * doing nothing.\n */\n get remoteAppPrimaryWindow() {\n let best = null;\n let bestArea = -1;\n for (const candidate of this.remoteAppWindows.values()) {\n if (!isPrimaryWindow(candidate)) continue;\n if (candidate.showState === SHOW_STATE.HIDDEN) continue;\n const area = (candidate.width || 0) * (candidate.height || 0);\n if (area > bestArea) {\n best = candidate;\n bestArea = area;\n }\n }\n return best;\n }\n\n #isFittedWindow(candidate) {\n const target = this.remoteAppPrimaryWindow;\n return Boolean(target) && target.windowId === candidate.windowId;\n }\n\n /** The fit always asks for the desktop's top-left corner. */\n #matchesFit(candidate) {\n const fit = this.remoteAppFit;\n if (!fit) return true;\n return Math.abs(candidate.x || 0) <= FIT_TOLERANCE_PIXELS &&\n Math.abs(candidate.y || 0) <= FIT_TOLERANCE_PIXELS &&\n Math.abs((candidate.width || 0) - fit.width) <= FIT_TOLERANCE_PIXELS &&\n Math.abs((candidate.height || 0) - fit.height) <= FIT_TOLERANCE_PIXELS;\n }\n\n #scheduleRemoteAppFit() {\n if (this.remoteAppFitTimer) return;\n this.remoteAppFitTimer = setTimeout(() => {\n this.remoteAppFitTimer = null;\n this.#applyRemoteAppFit();\n }, REMOTE_APP_FIT_DELAY_MS);\n }\n\n #applyRemoteAppFit() {\n const fit = this.remoteAppFit;\n if (!fit || !this.rail || this.state !== STATE.CONNECTED) return;\n\n // Maximize, whether the user asks for it or the application does at\n // startup, resizes into the work area, so it has to follow the browser\n // window even when there is no window to move yet.\n this.rail.setWorkArea(fit.width, fit.height);\n\n const target = this.remoteAppPrimaryWindow;\n if (!target || target.showState === SHOW_STATE.MINIMIZED) return;\n\n // A host that will not honour the move must not be asked forever: every\n // later window order would otherwise start the exchange again. A move that\n // did change the window is not counted, because a host that is responding\n // -- an application enforcing a minimum size, a restore still settling --\n // deserves the next attempt.\n const geometry = `${target.width || 0}x${target.height || 0}+${target.x || 0}+${target.y || 0}`;\n if (geometry !== fit.lastGeometry) fit.attempts = 0;\n fit.lastGeometry = geometry;\n if (fit.attempts >= REMOTE_APP_FIT_ATTEMPTS || fit.moves >= REMOTE_APP_FIT_MOVES) return;\n fit.attempts += 1;\n fit.moves += 1;\n\n if (target.showState === SHOW_STATE.MAXIMIZED) {\n // Windows ignores a move applied to a maximized window. Restoring it\n // first produces a window order, which schedules this fit again.\n console.info('[RDP] Restoring the maximized RemoteApp window before fitting it');\n this.rail.sysCommand(target.windowId, SYS_COMMAND.RESTORE);\n return;\n }\n\n console.info('[RDP] Fitting the RemoteApp window to the browser', {\n windowId: target.windowId,\n from: `${target.width || 0}x${target.height || 0}+${target.x || 0}+${target.y || 0}`,\n to: `${fit.width}x${fit.height}+0+0`,\n attempt: fit.attempts,\n });\n this.rail.activate(target.windowId);\n this.rail.moveWindow(target.windowId, {\n left: 0,\n top: 0,\n right: fit.width,\n bottom: fit.height,\n });\n }\n\n /**\n * Show only the primary window's own rectangle. RemoteApp paints nothing\n * outside its windows, so without this the canvas is the whole negotiated\n * desktop with the application somewhere inside it.\n */\n #publishRemoteAppViewport() {\n const target = this.remoteAppPrimaryWindow;\n if (!target || !target.width || !target.height) return;\n if (target.showState === SHOW_STATE.MINIMIZED || target.showState === SHOW_STATE.HIDDEN) return;\n\n const x = Math.max(0, Math.min(this.desktopWidth - 1, target.x || 0));\n const y = Math.max(0, Math.min(this.desktopHeight - 1, target.y || 0));\n const viewport = {\n x,\n y,\n width: Math.max(1, Math.min(this.desktopWidth - x, target.width)),\n height: Math.max(1, Math.min(this.desktopHeight - y, target.height)),\n };\n\n const previous = this.remoteAppViewport;\n if (previous && previous.x === viewport.x && previous.y === viewport.y &&\n previous.width === viewport.width && previous.height === viewport.height) {\n return;\n }\n this.remoteAppViewport = viewport;\n this.dispatchEvent(new CustomEvent('remoteappviewport', { detail: viewport }));\n }\n\n /**\n * Size the remote application's window to the room the browser has for it.\n *\n * The RDP desktop is fixed for the life of a connection -- resizing it needs\n * the dynamic virtual channel this client does not speak -- so the desktop is\n * negotiated large at connect time and the application window is moved and\n * resized inside it instead. The canvas then shows only that window.\n */\n fitRemoteApp(width: number, height: number) {\n if (!this.options.remoteApp?.program) return false;\n if (!Number.isFinite(width) || !Number.isFinite(height)) return false;\n\n const fit = {\n width: Math.max(MIN_REMOTE_APP_SIZE, Math.min(this.desktopWidth, Math.floor(width))),\n height: Math.max(MIN_REMOTE_APP_SIZE, Math.min(this.desktopHeight, Math.floor(height))),\n attempts: 0,\n moves: 0,\n lastGeometry: '',\n };\n this.remoteAppFit = fit;\n this.#scheduleRemoteAppFit();\n return true;\n }\n\n #markConnected() {\n if (this.state === STATE.CONNECTED) return;\n this.state = STATE.CONNECTED;\n\n console.info('[RDP] Session established', {\n desktop: `${this.desktopWidth}x${this.desktopHeight}`,\n encryption: this.security.encryptionEnabled ? `RC4 ${this.security.keys.keyLength * 8}-bit` : 'TLS transport',\n });\n\n this.dispatchEvent(new CustomEvent('ready', {\n detail: { width: this.desktopWidth, height: this.desktopHeight },\n }));\n\n // Nothing has been painted yet: the host only sends what changed, and the\n // session's first frame is a change the client has to ask for. Enabling\n // output first covers a host that starts the share suppressed.\n this.setOutputEnabled(true);\n this.refresh();\n }\n\n // --- fast-path output (defensive) ----------------------------------------\n\n /**\n * This client does not advertise fast-path output, so a well-behaved host\n * never uses it. Some do anyway, and dropping those frames would leave a\n * blank screen with no explanation.\n */\n #handleFastPath(frame: Uint8Array) {\n const encryptionFlags = (frame[0] >> 6) & 0x03;\n const lengthBytes = frame[1] & 0x80 ? 2 : 1;\n\n let payload = frame.subarray(1 + lengthBytes);\n if (encryptionFlags & 0x02) payload = this.security.decryptFastPath(payload);\n\n const reader = new Reader(payload);\n while (reader.remaining >= 3) {\n const header = reader.u8();\n const updateCode = header & 0x0f;\n const fragmentation = (header >> 4) & 0x03;\n const compression = (header >> 6) & 0x03;\n\n if (compression & 0x02) reader.u8(); // compressionFlags\n const size = reader.u16le();\n let updateData = reader.raw(Math.min(size, reader.remaining));\n\n // 0 single, 1 last, 2 first, 3 next\n if (fragmentation !== 0) {\n if (fragmentation === 2) this.fastPathFragments = [];\n this.fastPathFragments.push(updateData.slice());\n if (fragmentation !== 1) continue;\n updateData = concatBytes(...this.fastPathFragments);\n this.fastPathFragments = [];\n }\n\n this.#handleFastPathUpdate(updateCode, updateData);\n }\n }\n\n #handleFastPathUpdate(updateCode, updateData) {\n const reader = new Reader(updateData);\n\n switch (updateCode) {\n case 1: { // FASTPATH_UPDATETYPE_BITMAP\n reader.u16le(); // updateType, repeated inside the update\n const rects = parseBitmapUpdate(reader);\n if (rects.length > 0) {\n this.dispatchEvent(new CustomEvent('bitmap', { detail: { rects, palette: this.palette } }));\n }\n return;\n }\n case 2: // FASTPATH_UPDATETYPE_PALETTE\n this.palette = parsePaletteUpdate(reader);\n this.dispatchEvent(new CustomEvent('palette', { detail: this.palette }));\n return;\n case 5: // FASTPATH_UPDATETYPE_PTR_NULL\n this.dispatchEvent(new CustomEvent('pointer', { detail: { kind: 'system', hidden: true } }));\n return;\n case 6: // FASTPATH_UPDATETYPE_PTR_DEFAULT\n this.dispatchEvent(new CustomEvent('pointer', { detail: { kind: 'system', hidden: false } }));\n return;\n case 8: // FASTPATH_UPDATETYPE_PTR_POSITION\n this.dispatchEvent(new CustomEvent('pointer', {\n detail: { kind: 'position', x: reader.u16le(), y: reader.u16le() },\n }));\n return;\n case 9: // FASTPATH_UPDATETYPE_COLOR\n case 0x0a: // FASTPATH_UPDATETYPE_CACHED\n case 0x0b: { // FASTPATH_UPDATETYPE_POINTER\n // The slow-path parser expects the message type these updates imply.\n const messageType = { 9: 0x06, 0x0a: 0x07, 0x0b: 0x08 }[updateCode];\n const header = new Uint8Array([messageType, 0x00, 0x00, 0x00]);\n this.dispatchEvent(new CustomEvent('pointer', {\n detail: parsePointerUpdate(new Reader(concatBytes(header, updateData))),\n }));\n return;\n }\n default:\n return;\n }\n }\n\n // --- virtual channels -----------------------------------------------------\n\n #handleChannelData(name: string, bytes: Uint8Array) {\n let reassembler = this.channelReassemblers.get(name);\n if (!reassembler) {\n reassembler = new ChannelReassembler();\n this.channelReassemblers.set(name, reassembler);\n }\n\n const message = reassembler.push(bytes);\n if (!message) return;\n\n if (name === CLIPBOARD_CHANNEL.name) {\n this.#clipboardChannel().receive(message);\n return;\n }\n\n if (name === RAIL_CHANNEL.name) {\n this.#railChannel().receive(message);\n }\n }\n\n #sendVirtualChannel(name: string, bytes: Uint8Array) {\n const channelId = [...this.channelNamesById].find(([, channelName]) => channelName === name)?.[0];\n if (channelId === undefined) return false;\n\n for (const chunk of chunkChannelData(bytes, CHANNEL_CHUNK_LENGTH, CHANNEL_FLAG_SHOW_PROTOCOL)) {\n this.#send(sendDataRequest(this.userId, channelId, this.security.wrap(chunk)));\n }\n return true;\n }\n\n #railChannel() {\n if (this.rail) return this.rail;\n\n this.rail = new RailChannel({\n application: this.options.remoteApp,\n // The work area is what the host maximizes into, so it is the browser's\n // room for the window rather than the whole negotiated desktop. A fit\n // requested before the channel opened is already known here.\n width: this.remoteAppFit?.width || this.desktopWidth,\n height: this.remoteAppFit?.height || this.desktopHeight,\n send: (bytes: Uint8Array) => this.#sendVirtualChannel(RAIL_CHANNEL.name, bytes),\n onReady: ({ buildNumber, extended, flags }) => {\n console.info('[RDP] RemoteApp channel ready', { buildNumber, extended: Boolean(extended), flags: flags || 0 });\n this.dispatchEvent(new CustomEvent('remoteappready', {\n detail: { application: this.options.remoteApp },\n }));\n },\n onResult: (result) => {\n const detail = { ...result, application: this.options.remoteApp };\n this.dispatchEvent(new CustomEvent('remoteappresult', { detail }));\n if (!result.ok) this.#fail(new Error(result.message));\n else console.info('[RDP] Remote application launched', detail);\n },\n });\n\n return this.rail;\n }\n\n /** Created on first use, so a host without the channel costs nothing. */\n #clipboardChannel() {\n if (this.clipboard) return this.clipboard;\n\n this.clipboard = new ClipboardChannel({\n send: (bytes: Uint8Array) => this.#sendVirtualChannel(CLIPBOARD_CHANNEL.name, bytes),\n log: (step: number, detail) => console.info(`[RDP] ${step}`, detail),\n });\n\n this.clipboard.addEventListener('text', (event) => {\n this.dispatchEvent(new CustomEvent('clipboard', { detail: event.detail }));\n });\n\n return this.clipboard;\n }\n\n /**\n * Hand the host our clipboard text. Resolves once the host has acknowledged\n * owning it, which is what makes it safe to send a paste keystroke next.\n */\n setClipboardText(text: string) {\n if (!this.connected || !this.channelNamesById.size) return Promise.resolve(false);\n return this.#clipboardChannel().setLocalText(text || '');\n }\n\n get clipboardShared() {\n return Boolean(this.clipboard?.ready);\n }\n\n /**\n * Deliver text into the session the way a person would: put it on the host's\n * clipboard, wait for the host to acknowledge owning it, then press the paste\n * shortcut. Waiting is what stops the keystroke from pasting the previous\n * contents. A host with no clipboard channel gets the text typed instead.\n */\n pasteToHost(text: string) {\n if (!text || !this.connected) return Promise.resolve(false);\n\n const pressPaste = () => {\n if (!this.connected) return;\n this.sendKeyByCode('ControlLeft', true);\n this.sendKeyByCode('KeyV', true);\n this.sendKeyByCode('KeyV', false);\n this.sendKeyByCode('ControlLeft', false);\n };\n\n return this.setClipboardText(text).then((acknowledged: boolean) => {\n if (acknowledged) pressPaste();\n else this.sendUnicode(text);\n return true;\n });\n }\n\n // --- outbound share PDUs --------------------------------------------------\n\n #sendShareControl(pduType, payload: Uint8Array) {\n this.#sendOnIoChannel(this.security.wrap(shareControlPdu(pduType, this.userId, payload)));\n }\n\n #sendShareData(pduType2, payload: Uint8Array) {\n this.#sendOnIoChannel(\n this.security.wrap(shareDataPdu(this.shareId, this.userId, pduType2, payload))\n );\n }\n\n // --- input ----------------------------------------------------------------\n\n get connected() {\n return this.state === STATE.CONNECTED;\n }\n\n #sendInput(events) {\n if (!this.connected || events.length === 0) return;\n this.#sendShareData(PDU_TYPE2.INPUT, inputEventsPdu(events));\n }\n\n sendKeyByCode(code: number, pressed: boolean) {\n const mapped = lookupScancode(code);\n if (!mapped) return false;\n this.#sendInput([scancodeEvent(mapped.scancode, pressed, mapped.extended)]);\n return true;\n }\n\n sendUnicode(text: string) {\n const events = [];\n for (const character of text) {\n for (let i = 0; i < character.length; i++) {\n const unit = character.charCodeAt(i);\n events.push(unicodeEvent(unit, true));\n events.push(unicodeEvent(unit, false));\n }\n }\n // Long pastes are split so no single PDU grows past the MCS limit.\n for (let i = 0; i < events.length; i += 40) this.#sendInput(events.slice(i, i + 40));\n }\n\n sendCtrlAltDel() {\n const events = CTRL_ALT_DEL.map(({ code, pressed }) => {\n const mapped = lookupScancode(code);\n return scancodeEvent(mapped.scancode, pressed, mapped.extended);\n });\n this.#sendInput(events);\n }\n\n sendMouseMove(x: number, y: number) {\n this.#sendInput([mouseEvent(POINTER_FLAG.MOVE, x, y)]);\n }\n\n sendMouseButton(button: number, pressed: boolean, x: number, y: number) {\n const buttonFlag = {\n 0: POINTER_FLAG.BUTTON1,\n 1: POINTER_FLAG.BUTTON3, // middle\n 2: POINTER_FLAG.BUTTON2, // right\n }[button];\n\n if (buttonFlag === undefined) {\n // Buttons 4 and 5 (back / forward) travel as extended mouse events.\n const extendedFlag = button === 3 ? 0x0001 : 0x0002;\n this.#sendInput([extendedMouseEvent(extendedFlag | (pressed ? POINTER_FLAG.DOWN : 0), x, y)]);\n return;\n }\n\n this.#sendInput([mouseEvent(buttonFlag | (pressed ? POINTER_FLAG.DOWN : 0), x, y)]);\n }\n\n sendWheel(delta: number, x: number, y: number, horizontal = false) {\n const flags = pointerWheelFlags(delta, horizontal);\n if (!flags) return;\n this.#sendInput([mouseEvent(flags, x, y)]);\n }\n\n /** Tell the host which lock keys are already on, so they do not drift. */\n sendKeyboardSync(toggleFlags: number) {\n // Remembered so the idle keepalive restates the real state rather than\n // quietly telling the host every lock key is off.\n this.toggleFlags = toggleFlags >>> 0;\n this.#sendInput([syncEvent(this.toggleFlags)]);\n }\n\n /** Ask the host to resend a region, used after the canvas is re-created. */\n refresh(rect) {\n if (!this.connected || !this.refreshRectSupported) return;\n this.#sendShareData(PDU_TYPE2.REFRESH_RECT, refreshRectPdu([\n rect || { left: 0, top: 0, right: this.desktopWidth - 1, bottom: this.desktopHeight - 1 },\n ]));\n }\n\n /** Pause or resume host output, e.g. while the tab is hidden. */\n setOutputEnabled(enabled: boolean) {\n if (!this.connected) return;\n this.#sendShareData(PDU_TYPE2.SUPPRESS_OUTPUT, suppressOutputPdu(enabled, {\n left: 0,\n top: 0,\n right: this.desktopWidth - 1,\n bottom: this.desktopHeight - 1,\n }));\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/protocols/rdp/cliprdr.ts"],
|
|
4
|
-
"sourcesContent": ["// Clipboard redirection (MS-RDPECLIP) on the \"cliprdr\" virtual channel.\n//\n// Text only, and deliberately so: CF_UNICODETEXT is what copy and paste between\n// a browser and a desktop actually needs, and the file-contents formats would\n// mean presenting a filesystem to the host.\n//\n// RDP clipboards use delayed rendering. Whoever just copied something only\n// announces which formats it holds; the bytes are fetched by the other side at\n// paste time. Both halves of that exchange live here:\n//\n// host copied host -> FORMAT_LIST, we -> FORMAT_DATA_REQUEST,\n// host -> FORMAT_DATA_RESPONSE, we emit 'text'\n// we copied we -> FORMAT_LIST, host -> FORMAT_DATA_REQUEST,\n// we -> FORMAT_DATA_RESPONSE from the cached text\n\nimport { Reader, Writer, concatBytes } from './buffer';\n\n/** CLIPRDR_HEADER msgType (MS-RDPECLIP 2.2.1). */\nconst MSG = {\n MONITOR_READY: 0x0001,\n FORMAT_LIST: 0x0002,\n FORMAT_LIST_RESPONSE: 0x0003,\n FORMAT_DATA_REQUEST: 0x0004,\n FORMAT_DATA_RESPONSE: 0x0005,\n CLIP_CAPS: 0x0007,\n};\n\nconst RESPONSE_OK = 0x0001;\nconst RESPONSE_FAIL = 0x0002;\nconst ASCII_NAMES = 0x0004;\n\n/** Standard Windows clipboard format ids. */\nconst CF_TEXT = 1;\nconst CF_UNICODETEXT = 13;\n\nconst CAPSTYPE_GENERAL = 0x0001;\nconst CAPS_VERSION_2 = 0x00000002;\nconst USE_LONG_FORMAT_NAMES = 0x00000002;\n\n/** Short format names are a fixed-width field, long ones are NUL-terminated. */\nconst SHORT_FORMAT_NAME_BYTES = 32;\n\n/**\n * How long to wait for the host to acknowledge a format list before pasting\n * anyway. The ack is what proves the host has invalidated its own clipboard, so\n * waiting for it is what keeps a paste from delivering the previous contents.\n */\nconst FORMAT_LIST_ACK_TIMEOUT_MS = 400;\n\nfunction cliprdrPdu(msgType, msgFlags: number, body = new Uint8Array(0)) {\n const writer = new Writer(body.length + 8);\n writer.u16le(msgType).u16le(msgFlags).u32le(body.length).raw(body);\n return writer.take();\n}\n\n/** UTF-16LE with the terminating NUL the format expects. */\nfunction encodeUnicodeText(text: string) {\n const writer = new Writer(text.length * 2 + 2);\n writer.utf16le(text);\n writer.u16le(0);\n return writer.take();\n}\n\nfunction decodeUnicodeText(bytes: Uint8Array) {\n const units = new Uint16Array(bytes.length >> 1);\n for (let i = 0; i < units.length; i++) {\n units[i] = bytes[i * 2] | (bytes[i * 2 + 1] << 8);\n }\n\n // Everything from the first NUL on is padding, not content.\n let end = units.indexOf(0);\n if (end === -1) end = units.length;\n\n let text = '';\n // Chunked so a large paste cannot overflow the argument list.\n for (let i = 0; i < end; i += 4096) {\n text += String.fromCharCode(...units.subarray(i, Math.min(i + 4096, end)));\n }\n return text;\n}\n\nfunction encodeAsciiText(text: string) {\n const writer = new Writer(text.length + 1);\n for (const character of text) {\n const code = character.codePointAt(0);\n writer.u8(code > 0xff ? 0x3f : code); // '?' for anything Latin-1 cannot hold\n }\n writer.u8(0);\n return writer.take();\n}\n\nfunction decodeAsciiText(bytes: Uint8Array) {\n let end = bytes.indexOf(0);\n if (end === -1) end = bytes.length;\n return String.fromCharCode(...bytes.subarray(0, end));\n}\n\n/**\n * One clipboard channel. Emits:\n * 'text' { text } -- the host's clipboard now holds this text\n */\nexport class ClipboardChannel extends EventTarget {\n private readonly send: (bytes: Uint8Array) => void;\n private readonly log: (message: string, detail?: unknown) => void;\n private ready: boolean;\n private longFormatNames: boolean;\n private localText: string;\n private pendingAck: { resolve: (acknowledged: boolean) => void } | null;\n\n /**\n * @param {(bytes: Uint8Array) => void} send delivers one complete channel message\n * @param {(step: string, detail?: unknown) => void} [log]\n */\n constructor({ send, log }) {\n super();\n\n this.send = send;\n this.log = log || (() => {});\n this.ready = false;\n this.longFormatNames = true;\n\n /** The text this side hands over when the host pastes. */\n this.localText = '';\n /** Resolves when the host acknowledges our most recent format list. */\n this.pendingAck = null;\n }\n\n #sendPdu(msgType, msgFlags: number, body: Uint8Array) {\n this.send(cliprdrPdu(msgType, msgFlags, body));\n }\n\n // --- outbound ------------------------------------------------------------\n\n /**\n * Announce that this side now owns the clipboard and holds `text`. Resolves\n * once the host has acknowledged, so a paste keystroke can safely follow.\n */\n setLocalText(text: string) {\n this.localText = text;\n if (!this.ready) return Promise.resolve(false);\n\n this.#sendPdu(MSG.FORMAT_LIST, 0, this.#formatListBody(\n text ? [CF_UNICODETEXT, CF_TEXT] : []\n ));\n\n // Only the newest list matters; an earlier waiter would paste stale text.\n if (this.pendingAck) this.pendingAck.resolve(false);\n\n return new Promise((resolve) => {\n let timer = 0;\n const settle = (acknowledged: boolean) => {\n clearTimeout(timer);\n if (this.pendingAck?.resolve === settle) this.pendingAck = null;\n resolve(acknowledged);\n };\n\n timer = setTimeout(() => {\n this.log('clipboard format list was not acknowledged');\n settle(false);\n }, FORMAT_LIST_ACK_TIMEOUT_MS);\n\n this.pendingAck = { resolve: settle };\n });\n }\n\n #formatListBody(formatIds) {\n const parts = formatIds.map((formatId) => {\n const writer = new Writer(this.longFormatNames ? 6 : 4 + SHORT_FORMAT_NAME_BYTES);\n writer.u32le(formatId);\n // Standard formats have no name; long lists still need its terminator.\n if (this.longFormatNames) writer.u16le(0);\n else writer.zeros(SHORT_FORMAT_NAME_BYTES);\n return writer.take();\n });\n\n return concatBytes(...parts);\n }\n\n // --- inbound -------------------------------------------------------------\n\n receive(bytes: Uint8Array) {\n if (bytes.length < 8) return;\n\n const reader = new Reader(bytes);\n const msgType = reader.u16le();\n const msgFlags = reader.u16le();\n const dataLength = reader.u32le();\n const rest = reader.rest();\n const body = rest.subarray(0, Math.min(dataLength, rest.length));\n\n switch (msgType) {\n case MSG.CLIP_CAPS:\n this.#handleCapabilities(body);\n return;\n\n case MSG.MONITOR_READY:\n this.#handleMonitorReady();\n return;\n\n case MSG.FORMAT_LIST:\n this.#handleFormatList(body, msgFlags);\n return;\n\n case MSG.FORMAT_LIST_RESPONSE:\n // A failed ack still unblocks a waiting paste: the keystroke is more\n // useful than a silent stall.\n this.pendingAck?.resolve((msgFlags & RESPONSE_FAIL) === 0);\n return;\n\n case MSG.FORMAT_DATA_REQUEST:\n this.#handleFormatDataRequest(body);\n return;\n\n case MSG.FORMAT_DATA_RESPONSE:\n this.#handleFormatDataResponse(body, msgFlags);\n return;\n\n default:\n return;\n }\n }\n\n #handleCapabilities(body: Uint8Array) {\n const reader = new Reader(body);\n if (reader.remaining < 4) return;\n\n const setCount = reader.u16le();\n reader.u16le(); // pad\n\n for (let i = 0; i < setCount && reader.remaining >= 4; i++) {\n const type = reader.u16le();\n const length = reader.u16le();\n if (length < 4 || length - 4 > reader.remaining) break;\n const set = new Reader(reader.raw(length - 4));\n\n if (type === CAPSTYPE_GENERAL && set.remaining >= 8) {\n set.u32le(); // version\n const flags = set.u32le();\n // Both sides have to agree, so the host's answer decides.\n this.longFormatNames = (flags & USE_LONG_FORMAT_NAMES) !== 0;\n }\n }\n }\n\n /**\n * The host is ready. The client answers with its own capabilities and an\n * initial format list, which MS-RDPECLIP 1.3.2.1 requires even when there is\n * nothing to offer yet.\n */\n #handleMonitorReady() {\n const general = new Writer(12);\n general.u16le(CAPSTYPE_GENERAL).u16le(12);\n general.u32le(CAPS_VERSION_2);\n general.u32le(this.longFormatNames ? USE_LONG_FORMAT_NAMES : 0);\n\n const caps = new Writer(20);\n caps.u16le(1).u16le(0); // one capability set, then padding\n caps.raw(general.take());\n\n this.#sendPdu(MSG.CLIP_CAPS, 0, caps.take());\n this.ready = true;\n this.#sendPdu(MSG.FORMAT_LIST, 0, this.#formatListBody(\n this.localText ? [CF_UNICODETEXT, CF_TEXT] : []\n ));\n\n this.log('clipboard channel ready', { longFormatNames: this.longFormatNames });\n }\n\n #handleFormatList(body: Uint8Array, msgFlags: number) {\n const formats = this.#parseFormatList(body, msgFlags);\n this.#sendPdu(MSG.FORMAT_LIST_RESPONSE, RESPONSE_OK);\n\n // Unicode first: it is the only format that survives every character.\n const wanted = formats.includes(CF_UNICODETEXT)\n ? CF_UNICODETEXT\n : formats.includes(CF_TEXT) ? CF_TEXT : null;\n\n if (wanted === null) {\n this.log('host clipboard holds no text format', { formats });\n return;\n }\n\n const request = new Writer(4);\n request.u32le(wanted);\n this.#sendPdu(MSG.FORMAT_DATA_REQUEST, 0, request.take());\n }\n\n #parseFormatList(body: Uint8Array, msgFlags: number) {\n const reader = new Reader(body);\n const formats = [];\n\n // A short list is a fixed 36 bytes per entry; a long one is variable, so\n // the encoding has to be known rather than guessed. ASCII names are always\n // the short form (MS-RDPECLIP 2.2.3.1.1.1).\n const short = !this.longFormatNames || (msgFlags & ASCII_NAMES) !== 0;\n\n while (reader.remaining >= 4) {\n formats.push(reader.u32le());\n\n if (short) {\n if (reader.remaining < SHORT_FORMAT_NAME_BYTES) break;\n reader.skip(SHORT_FORMAT_NAME_BYTES);\n continue;\n }\n\n // Long names are UTF-16LE up to and including a NUL unit.\n while (reader.remaining >= 2 && reader.u16le() !== 0) { /* skip the name */ }\n }\n\n return formats;\n }\n\n #handleFormatDataRequest(body: Uint8Array) {\n const reader = new Reader(body);\n const formatId = reader.remaining >= 4 ? reader.u32le() : 0;\n\n if (formatId !== CF_UNICODETEXT && formatId !== CF_TEXT) {\n this.#sendPdu(MSG.FORMAT_DATA_RESPONSE, RESPONSE_FAIL);\n return;\n }\n\n const data = formatId === CF_UNICODETEXT\n ? encodeUnicodeText(this.localText)\n : encodeAsciiText(this.localText);\n\n this.#sendPdu(MSG.FORMAT_DATA_RESPONSE, RESPONSE_OK, data);\n this.log('handed the host our clipboard', { formatId, characters: this.localText.length });\n }\n\n #handleFormatDataResponse(body: Uint8Array, msgFlags: number) {\n if (msgFlags & RESPONSE_FAIL) {\n this.log('the host refused to hand over its clipboard');\n return;\n }\n\n // Whichever format was asked for, a UTF-16 payload has a zero in every\n // second byte; that is what tells the two apart on the way back.\n const looksUnicode = body.length >= 2 && body.length % 2 === 0 && body[1] === 0;\n const text = looksUnicode ? decodeUnicodeText(body) : decodeAsciiText(body);\n if (!text) return;\n\n this.dispatchEvent(new CustomEvent('text', { detail: { text } }));\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|