ai-remote 0.1.0

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.
Files changed (114) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +7 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/protocols/index.js +35 -0
  5. package/dist/protocols/index.js.map +7 -0
  6. package/dist/protocols/rdp/buffer.js +170 -0
  7. package/dist/protocols/rdp/buffer.js.map +7 -0
  8. package/dist/protocols/rdp/caps.js +260 -0
  9. package/dist/protocols/rdp/caps.js.map +7 -0
  10. package/dist/protocols/rdp/cert.js +135 -0
  11. package/dist/protocols/rdp/cert.js.map +7 -0
  12. package/dist/protocols/rdp/client.js +1068 -0
  13. package/dist/protocols/rdp/client.js.map +7 -0
  14. package/dist/protocols/rdp/cliprdr.js +240 -0
  15. package/dist/protocols/rdp/cliprdr.js.map +7 -0
  16. package/dist/protocols/rdp/credssp.js +254 -0
  17. package/dist/protocols/rdp/credssp.js.map +7 -0
  18. package/dist/protocols/rdp/crypto.js +369 -0
  19. package/dist/protocols/rdp/crypto.js.map +7 -0
  20. package/dist/protocols/rdp/display.js +589 -0
  21. package/dist/protocols/rdp/display.js.map +7 -0
  22. package/dist/protocols/rdp/gcc.js +132 -0
  23. package/dist/protocols/rdp/gcc.js.map +7 -0
  24. package/dist/protocols/rdp/keymap.js +132 -0
  25. package/dist/protocols/rdp/keymap.js.map +7 -0
  26. package/dist/protocols/rdp/mcs.js +223 -0
  27. package/dist/protocols/rdp/mcs.js.map +7 -0
  28. package/dist/protocols/rdp/ntlm.js +305 -0
  29. package/dist/protocols/rdp/ntlm.js.map +7 -0
  30. package/dist/protocols/rdp/pdu.js +444 -0
  31. package/dist/protocols/rdp/pdu.js.map +7 -0
  32. package/dist/protocols/rdp/rail.js +383 -0
  33. package/dist/protocols/rdp/rail.js.map +7 -0
  34. package/dist/protocols/rdp/rle.js +276 -0
  35. package/dist/protocols/rdp/rle.js.map +7 -0
  36. package/dist/protocols/rdp/sec.js +156 -0
  37. package/dist/protocols/rdp/sec.js.map +7 -0
  38. package/dist/protocols/rdp/session.js +307 -0
  39. package/dist/protocols/rdp/session.js.map +7 -0
  40. package/dist/protocols/rdp/tls.js +354 -0
  41. package/dist/protocols/rdp/tls.js.map +7 -0
  42. package/dist/protocols/rdp/vchannel.js +59 -0
  43. package/dist/protocols/rdp/vchannel.js.map +7 -0
  44. package/dist/protocols/rdp/x224.js +110 -0
  45. package/dist/protocols/rdp/x224.js.map +7 -0
  46. package/dist/protocols/ssh/kex.js +177 -0
  47. package/dist/protocols/ssh/kex.js.map +7 -0
  48. package/dist/protocols/ssh/messages.js +55 -0
  49. package/dist/protocols/ssh/messages.js.map +7 -0
  50. package/dist/protocols/ssh/session.js +608 -0
  51. package/dist/protocols/ssh/session.js.map +7 -0
  52. package/dist/protocols/ssh/terminal.js +191 -0
  53. package/dist/protocols/ssh/terminal.js.map +7 -0
  54. package/dist/protocols/ssh/transport.js +611 -0
  55. package/dist/protocols/ssh/transport.js.map +7 -0
  56. package/dist/protocols/ssh/wire.js +189 -0
  57. package/dist/protocols/ssh/wire.js.map +7 -0
  58. package/dist/protocols/types.js +1 -0
  59. package/dist/protocols/types.js.map +7 -0
  60. package/dist/protocols/vnc/input.js +16 -0
  61. package/dist/protocols/vnc/input.js.map +7 -0
  62. package/dist/protocols/vnc/session.js +259 -0
  63. package/dist/protocols/vnc/session.js.map +7 -0
  64. package/dist/shared/connection.js +114 -0
  65. package/dist/shared/connection.js.map +7 -0
  66. package/dist/shared/device.js +1 -0
  67. package/dist/shared/device.js.map +7 -0
  68. package/dist/shared/hosts.js +133 -0
  69. package/dist/shared/hosts.js.map +7 -0
  70. package/dist/shared/icons.js +42 -0
  71. package/dist/shared/icons.js.map +7 -0
  72. package/dist/shared/protocol.js +40 -0
  73. package/dist/shared/protocol.js.map +7 -0
  74. package/dist/shared/signals.js +42 -0
  75. package/dist/shared/signals.js.map +7 -0
  76. package/package.json +82 -0
  77. package/src/index.ts +14 -0
  78. package/src/protocols/index.ts +72 -0
  79. package/src/protocols/rdp/buffer.ts +204 -0
  80. package/src/protocols/rdp/caps.ts +304 -0
  81. package/src/protocols/rdp/cert.ts +190 -0
  82. package/src/protocols/rdp/client.ts +1363 -0
  83. package/src/protocols/rdp/cliprdr.ts +344 -0
  84. package/src/protocols/rdp/credssp.ts +319 -0
  85. package/src/protocols/rdp/crypto.ts +416 -0
  86. package/src/protocols/rdp/display.ts +750 -0
  87. package/src/protocols/rdp/gcc.ts +162 -0
  88. package/src/protocols/rdp/keymap.ts +68 -0
  89. package/src/protocols/rdp/mcs.ts +283 -0
  90. package/src/protocols/rdp/ntlm.ts +380 -0
  91. package/src/protocols/rdp/pdu.ts +497 -0
  92. package/src/protocols/rdp/rail.ts +480 -0
  93. package/src/protocols/rdp/rle.ts +343 -0
  94. package/src/protocols/rdp/sec.ts +184 -0
  95. package/src/protocols/rdp/session.ts +393 -0
  96. package/src/protocols/rdp/tls.ts +452 -0
  97. package/src/protocols/rdp/vchannel.ts +89 -0
  98. package/src/protocols/rdp/x224.ts +139 -0
  99. package/src/protocols/ssh/kex.ts +227 -0
  100. package/src/protocols/ssh/messages.ts +54 -0
  101. package/src/protocols/ssh/session.ts +878 -0
  102. package/src/protocols/ssh/terminal.ts +242 -0
  103. package/src/protocols/ssh/transport.ts +732 -0
  104. package/src/protocols/ssh/wire.ts +220 -0
  105. package/src/protocols/types.ts +146 -0
  106. package/src/protocols/vnc/input.ts +13 -0
  107. package/src/protocols/vnc/novnc.d.ts +75 -0
  108. package/src/protocols/vnc/session.ts +367 -0
  109. package/src/shared/connection.ts +206 -0
  110. package/src/shared/device.ts +28 -0
  111. package/src/shared/hosts.ts +240 -0
  112. package/src/shared/icons.ts +49 -0
  113. package/src/shared/protocol.ts +55 -0
  114. package/src/shared/signals.ts +67 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/protocols/rdp/display.ts"],
4
+ "sourcesContent": ["// Canvas rendering and input capture for an RDP session.\n//\n// The host sends rectangles of pixels and a cursor shape; this module paints\n// them, maps browser events back into desktop coordinates, and turns key events\n// into the scancodes RDP expects.\n\nimport { decodeBitmapRect } from './rle';\nimport { TOGGLE_FLAG } from './pdu';\n\n/**\n * On a Mac the editing shortcuts live on Command, and Windows expects them on\n * Control. Sending Command through as the Windows key -- which is what its\n * scancode means -- would make copy and paste unreachable, so it is translated\n * instead. The Special Keys panel still sends a real Windows key.\n */\nconst IS_MAC = /Mac|iPod|iPhone|iPad/.test(\n globalThis.navigator?.platform || globalThis.navigator?.userAgent || ''\n);\n\nconst COMMAND_TO_CONTROL = {\n MetaLeft: 'ControlLeft',\n MetaRight: 'ControlRight',\n};\n\n// Trackpads emit many small wheel events. Waiting for roughly one line of\n// accumulated motion prevents each tiny event from becoming a full RDP notch.\nconst WHEEL_STEP_PIXELS = 60;\nconst WHEEL_LINE_PIXELS = 16;\n\n/** Two fingers dragged this far, per notch, count as one wheel click. */\nconst TOUCH_SCROLL_STEP = 20;\n\n/**\n * How far past \"the whole desktop is visible\" a pinch may go. Eight is well\n * past readable on any phone; the floor of 1 is the fitted view, because\n * zooming out beyond the window only adds letterboxing.\n */\nconst MAX_ZOOM = 8;\nconst MIN_ZOOM = 1;\n\n\nexport class RdpDisplay {\n constructor(container) {\n this.container = container;\n this.canvas = document.createElement('canvas');\n this.canvas.tabIndex = 0;\n this.canvas.style.display = 'block';\n this.canvas.style.position = 'absolute';\n this.canvas.style.outline = 'none';\n this.canvas.style.imageRendering = 'auto';\n this.canvas.style.boxShadow = 'none';\n this.context = this.canvas.getContext('2d', { alpha: false });\n\n // The canvas is always the whole remote desktop, and this element decides\n // how much of it is on screen. A full-desktop session shows all of it; a\n // RemoteApp session shows only the application's own window, because\n // RemoteApp paints nothing around it.\n this.viewportElement = document.createElement('div');\n this.viewportElement.style.position = 'relative';\n this.viewportElement.style.overflow = 'hidden';\n this.viewportElement.style.margin = 'auto';\n this.viewportElement.style.background = '#000';\n this.viewportElement.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.8)';\n this.viewportElement.appendChild(this.canvas);\n\n container.replaceChildren(this.viewportElement);\n\n // The element that actually scrolls when the desktop is larger than the\n // window. `container` grows with its contents, so it is the wrong thing to\n // measure the available space against.\n this.scrollHost = container.parentElement || container;\n\n this.width = 0;\n this.height = 0;\n this.viewport = { x: 0, y: 0, width: 0, height: 0 };\n this.scale = 1;\n this.scaleViewport = true;\n // A multiplier on top of the fitted scale. 1 shows the whole desktop.\n this.zoom = 1;\n // Once the user has pinched, their zoom is theirs to keep: rotating the\n // phone should not silently throw it away.\n this.userZoomed = false;\n this.viewOnly = false;\n this.client = null;\n this.palette = null;\n this.cursorCache = new Map();\n this.heldKeys = new Set();\n this.keyboardSynced = false;\n\n // Turning the phone changes which axis is the tight one; applyScale picks\n // the opening zoom up again unless the user has since chosen their own.\n this.onWindowResize = () => this.applyScale();\n window.addEventListener('resize', this.onWindowResize);\n\n // The workspace also changes width without the window changing at all --\n // the SSH panel docking beside it, for one -- and no `resize` is fired for\n // that, so the desktop would keep the scale it had before the panel opened.\n this.resizeObserver = typeof ResizeObserver === 'function'\n ? new ResizeObserver(() => this.applyScale())\n : null;\n this.resizeObserver?.observe(this.scrollHost);\n }\n\n resize(width: number, height: number) {\n this.width = width;\n this.height = height;\n this.canvas.width = width;\n this.canvas.height = height;\n\n this.context.fillStyle = '#000';\n this.context.fillRect(0, 0, width, height);\n this.setViewport({ x: 0, y: 0, width, height });\n }\n\n /**\n * Choose the part of the desktop that is on screen. Everything else about\n * the session -- painting, input coordinates, the remote pointer -- keeps\n * working in desktop coordinates, so only the framing changes here.\n */\n setViewport({ x = 0, y = 0, width, height } = {}) {\n if (!this.width || !this.height) return;\n\n const left = Math.max(0, Math.min(this.width - 1, Math.floor(x)));\n const top = Math.max(0, Math.min(this.height - 1, Math.floor(y)));\n this.viewport = {\n x: left,\n y: top,\n width: Math.max(1, Math.min(this.width - left, Math.floor(width || this.width))),\n height: Math.max(1, Math.min(this.height - top, Math.floor(height || this.height))),\n };\n\n // The toolbar reads the resolution off the canvas, and a screenshot of a\n // RemoteApp session should be the window rather than the desktop it sits on.\n this.canvas.dataset.viewport = [\n this.viewport.x, this.viewport.y, this.viewport.width, this.viewport.height,\n ].join(',');\n this.applyScale();\n }\n\n /** The space a fitted desktop has to fit into: the window, not the content. */\n #available() {\n const host = this.scrollHost;\n return {\n width: host.clientWidth || this.container.clientWidth || 1,\n height: host.clientHeight || this.container.clientHeight || 1,\n };\n }\n\n /** The scale at which the whole desktop is on screen, letterboxed. */\n #fitScale(available = this.#available()) {\n const fitted = Math.min(\n available.width / this.viewport.width,\n available.height / this.viewport.height\n );\n return Number.isFinite(fitted) && fitted > 0 ? fitted : 1;\n }\n\n /**\n * The zoom to open a session at.\n *\n * On a laptop the fitted view is the right one: the whole desktop is on\n * screen and readable, and that is where this stays. On a phone it is not --\n * a 1024x768 desktop fitted to a 375pt-wide window lands at about a third of\n * full size, a band of unreadable pixels across the middle of a tall black\n * screen. When fitting would land under LEGIBLE_SCALE the desktop is scaled\n * to fill the window instead: the short edge matches exactly and the long one\n * is panned. Everything is still reachable, and a pinch still gets back to\n * the whole desktop. Legible first and complete second is the right way round\n * on a screen this size.\n */\n #defaultZoom() {\n return 1;\n }\n\n applyScale() {\n if (!this.width || !this.height) return;\n const viewport = this.viewport;\n\n // Measured once and shared, so the zoom and the scale it multiplies can\n // never come from two different layouts. Turning the phone fires `resize`\n // while the new size is still settling, and reading it twice there is how\n // the desktop ends up fitted on one axis and filled on the other.\n const available = this.#available();\n const fitted = this.#fitScale(available);\n if (!this.userZoomed) this.zoom = this.#defaultZoom();\n\n const scale = (this.scaleViewport ? fitted : 1) * this.zoom;\n this.scale = scale;\n\n // Sized in exact fractions of a pixel rather than whole ones: rounding the\n // two axes apart is what makes a scaled desktop very slightly the wrong\n // shape, which is visible as soon as the scale factor is small -- a phone\n // fitting a 1024x768 desktop into 375 points, for instance.\n this.viewportElement.style.width = `${viewport.width * scale}px`;\n this.viewportElement.style.height = `${viewport.height * scale}px`;\n this.canvas.style.width = `${this.width * scale}px`;\n this.canvas.style.height = `${this.height * scale}px`;\n this.canvas.style.left = `${-viewport.x * scale}px`;\n this.canvas.style.top = `${-viewport.y * scale}px`;\n }\n\n /**\n * Zoom about a point, keeping whatever is under it in place. `focal` is in\n * client coordinates -- the midpoint between two fingers, usually.\n */\n setZoom(next, focal = null) {\n const clamped = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, next));\n if (!Number.isFinite(clamped) || clamped === this.zoom) return;\n\n const host = this.scrollHost;\n const before = this.scale;\n const bounds = host.getBoundingClientRect();\n const anchorX = focal ? focal.x - bounds.left : bounds.width / 2;\n const anchorY = focal ? focal.y - bounds.top : bounds.height / 2;\n const contentX = host.scrollLeft + anchorX;\n const contentY = host.scrollTop + anchorY;\n\n this.zoom = clamped;\n this.applyScale();\n\n const ratio = before > 0 ? this.scale / before : 1;\n host.scrollLeft = contentX * ratio - anchorX;\n host.scrollTop = contentY * ratio - anchorY;\n }\n\n /** Move the visible window over the desktop. Returns what it actually used. */\n panBy(dx, dy) {\n const host = this.scrollHost;\n const beforeX = host.scrollLeft;\n const beforeY = host.scrollTop;\n host.scrollLeft = beforeX - dx;\n host.scrollTop = beforeY - dy;\n return {\n x: beforeX - host.scrollLeft,\n y: beforeY - host.scrollTop,\n };\n }\n\n /** True when the desktop is larger than the window on either axis. */\n canPan() {\n if (this.zoom <= 1.05) return false;\n const host = this.scrollHost;\n return host.scrollWidth > host.clientWidth + 2 || host.scrollHeight > host.clientHeight + 2;\n }\n\n setScaleViewport(enabled: boolean) {\n this.scaleViewport = enabled;\n if (!enabled) this.userZoomed = false;\n this.applyScale();\n }\n\n /** Back to the opening view: whole desktop fitted on screen. */\n resetZoom() {\n this.userZoomed = false;\n this.zoom = 1;\n this.applyScale();\n const host = this.scrollHost;\n host.scrollLeft = 0;\n host.scrollTop = 0;\n }\n\n drawRects(rects, palette) {\n if (palette) this.palette = palette;\n\n for (const rect of rects) {\n if (rect.width === 0 || rect.height === 0) continue;\n try {\n const rgba = decodeBitmapRect(rect, this.palette);\n const image = new ImageData(rgba, rect.width, rect.height);\n this.context.putImageData(image, rect.left, rect.top);\n } catch (error) {\n // One malformed rectangle must not kill the session; the next full\n // update repaints the area anyway.\n console.warn('[RDP] Dropped an undecodable bitmap rectangle', {\n geometry: `${rect.width}x${rect.height}+${rect.left}+${rect.top}`,\n bitsPerPixel: rect.bitsPerPixel,\n compressed: rect.compressed,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n\n // --- cursor ---------------------------------------------------------------\n\n /**\n * Turn an RDP pointer bitmap into a CSS cursor. RDP stores both masks\n * bottom-up with rows padded to an even number of bytes, and a set AND-mask\n * bit means \"leave this pixel alone\", which becomes transparency here.\n */\n #cursorFromPointer(pointer) {\n const { width, height, xorBpp, xorMask, andMask, hotSpotX, hotSpotY } = pointer;\n if (!width || !height) return null;\n\n const xorBytesPerRow = Math.ceil((width * xorBpp) / 8 / 2) * 2;\n const andBytesPerRow = Math.ceil(width / 8 / 2) * 2;\n const rgba = new Uint8ClampedArray(width * height * 4);\n\n for (let y = 0; y < height; y++) {\n const sourceRow = height - 1 - y; // bottom-up\n for (let x = 0; x < width; x++) {\n const andByte = andMask[sourceRow * andBytesPerRow + (x >> 3)];\n const transparent = andByte !== undefined && (andByte & (0x80 >> (x & 7))) !== 0;\n\n let r = 0;\n let g = 0;\n let b = 0;\n let a = transparent ? 0 : 255;\n\n if (xorBpp === 1) {\n const bit = xorMask[sourceRow * xorBytesPerRow + (x >> 3)] & (0x80 >> (x & 7));\n r = g = b = bit ? 255 : 0;\n } else {\n const bytesPerPixel = xorBpp / 8;\n const offset = sourceRow * xorBytesPerRow + x * bytesPerPixel;\n if (xorBpp === 16) {\n const value = xorMask[offset] | (xorMask[offset + 1] << 8);\n r = ((value >> 11) & 0x1f) * 255 / 31;\n g = ((value >> 5) & 0x3f) * 255 / 63;\n b = (value & 0x1f) * 255 / 31;\n } else {\n b = xorMask[offset] || 0;\n g = xorMask[offset + 1] || 0;\n r = xorMask[offset + 2] || 0;\n if (xorBpp === 32 && xorMask[offset + 3] !== undefined && andMask.length === 0) {\n a = xorMask[offset + 3];\n }\n }\n }\n\n const target = (y * width + x) * 4;\n rgba[target] = r;\n rgba[target + 1] = g;\n rgba[target + 2] = b;\n rgba[target + 3] = a;\n }\n }\n\n const cursorCanvas = document.createElement('canvas');\n cursorCanvas.width = width;\n cursorCanvas.height = height;\n cursorCanvas.getContext('2d').putImageData(new ImageData(rgba, width, height), 0, 0);\n\n const hotX = Math.min(hotSpotX || 0, width - 1);\n const hotY = Math.min(hotSpotY || 0, height - 1);\n return `url(${cursorCanvas.toDataURL('image/png')}) ${hotX} ${hotY}, default`;\n }\n\n applyPointer(pointer) {\n if (pointer.kind === 'system') {\n this.canvas.style.cursor = pointer.hidden ? 'none' : 'default';\n return;\n }\n\n if (pointer.kind === 'position') {\n // Host-driven pointer warps cannot move the real mouse; ignore them and\n // let the local pointer stay where the user put it.\n return;\n }\n\n if (pointer.kind === 'cached') {\n const cached = this.cursorCache.get(pointer.cacheIndex);\n if (cached) this.canvas.style.cursor = cached;\n return;\n }\n\n if (pointer.kind === 'bitmap') {\n try {\n const cursor = this.#cursorFromPointer(pointer);\n if (!cursor) return;\n this.cursorCache.set(pointer.cacheIndex, cursor);\n this.canvas.style.cursor = cursor;\n } catch (error) {\n console.warn('[RDP] Could not build a cursor from the host pointer update', error);\n }\n }\n }\n\n // --- input ----------------------------------------------------------------\n\n #toRemote(event) {\n const bounds = this.canvas.getBoundingClientRect();\n const scaleX = this.width / (bounds.width || 1);\n const scaleY = this.height / (bounds.height || 1);\n return {\n x: Math.max(0, Math.min(this.width - 1, (event.clientX - bounds.left) * scaleX)),\n y: Math.max(0, Math.min(this.height - 1, (event.clientY - bounds.top) * scaleY)),\n };\n }\n\n #syncKeyboardOnce(event) {\n if (this.keyboardSynced || !this.client) return;\n this.keyboardSynced = true;\n\n let flags = 0;\n if (event.getModifierState?.('CapsLock')) flags |= TOGGLE_FLAG.CAPS_LOCK;\n if (event.getModifierState?.('NumLock')) flags |= TOGGLE_FLAG.NUM_LOCK;\n if (event.getModifierState?.('ScrollLock')) flags |= TOGGLE_FLAG.SCROLL_LOCK;\n this.client.sendKeyboardSync(flags);\n }\n\n attachInput(client) {\n this.client = client;\n this.keyboardSynced = false;\n\n const canSend = () => this.client && this.client.connected && !this.viewOnly;\n\n let pendingMove = null;\n let moveScheduled = false;\n let pendingWheelDelta = 0;\n let touchIsDown = false;\n let lastTouchX = 0;\n let lastTouchY = 0;\n let lastTouchScrollY = 0;\n let lastTouchScrollX = 0;\n let gestureStartMid = { x: 0, y: 0 };\n let gestureStartDistance = 0;\n let lastPinchDistance = 0;\n let pendingTouchWheelDelta = 0;\n let touchMode = null;\n\n const touchMidpoint = (touches) => ({\n x: (touches[0].clientX + touches[1].clientX) / 2,\n y: (touches[0].clientY + touches[1].clientY) / 2,\n });\n\n const touchDistance = (touches) => Math.hypot(\n touches[0].clientX - touches[1].clientX,\n touches[0].clientY - touches[1].clientY\n );\n const flushMove = () => {\n moveScheduled = false;\n if (pendingMove && canSend()) this.client.sendMouseMove(pendingMove.x, pendingMove.y);\n pendingMove = null;\n };\n\n this.handlers = {\n mousemove: (event) => {\n if (!canSend()) return;\n pendingMove = this.#toRemote(event);\n if (!moveScheduled) {\n moveScheduled = true;\n requestAnimationFrame(flushMove);\n }\n },\n mousedown: (event) => {\n this.canvas.focus();\n if (!canSend()) return;\n event.preventDefault();\n const { x, y } = this.#toRemote(event);\n this.client.sendMouseButton(event.button, true, x, y);\n },\n mouseup: (event) => {\n if (!canSend()) return;\n event.preventDefault();\n const { x, y } = this.#toRemote(event);\n this.client.sendMouseButton(event.button, false, x, y);\n },\n contextmenu: (event) => event.preventDefault(),\n wheel: (event) => {\n if (!canSend()) return;\n event.preventDefault();\n\n let delta = event.deltaY;\n if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) delta *= WHEEL_LINE_PIXELS;\n if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) {\n delta *= Math.max(1, this.canvas.clientHeight || this.height);\n }\n if (!delta) return;\n\n // Do not let a partial gesture in one direction accelerate the next\n // gesture in the opposite direction.\n if (pendingWheelDelta && Math.sign(pendingWheelDelta) !== Math.sign(delta)) {\n pendingWheelDelta = 0;\n }\n pendingWheelDelta += delta;\n if (Math.abs(pendingWheelDelta) < WHEEL_STEP_PIXELS) return;\n\n const direction = Math.sign(pendingWheelDelta);\n pendingWheelDelta -= direction * WHEEL_STEP_PIXELS;\n const { x, y } = this.#toRemote(event);\n this.client.sendWheel(direction, x, y);\n },\n touchstart: (event) => {\n this.canvas.focus();\n if (!canSend()) return;\n event.preventDefault();\n\n if (event.touches.length === 1) {\n touchMode = 'tap';\n const touch = event.touches[0];\n lastTouchX = touch.clientX;\n lastTouchY = touch.clientY;\n const { x, y } = this.#toRemote(touch);\n this.client.sendMouseMove(x, y);\n this.client.sendMouseButton(0, true, x, y);\n touchIsDown = true;\n } else if (event.touches.length === 2) {\n touchMode = 'undecided';\n if (touchIsDown) {\n const { x, y } = this.#toRemote({ clientX: lastTouchX, clientY: lastTouchY });\n this.client.sendMouseButton(0, false, x, y);\n touchIsDown = false;\n }\n const mid = touchMidpoint(event.touches);\n const dist = touchDistance(event.touches);\n gestureStartMid = { x: mid.x, y: mid.y };\n gestureStartDistance = dist;\n lastTouchScrollY = mid.y;\n lastTouchScrollX = mid.x;\n lastPinchDistance = dist;\n pendingTouchWheelDelta = 0;\n }\n },\n touchmove: (event) => {\n if (!canSend()) return;\n event.preventDefault();\n\n if (event.touches.length === 1 && touchMode === 'tap') {\n const touch = event.touches[0];\n lastTouchX = touch.clientX;\n lastTouchY = touch.clientY;\n const { x, y } = this.#toRemote(touch);\n this.client.sendMouseMove(x, y);\n } else if (event.touches.length === 2) {\n const mid = touchMidpoint(event.touches);\n const currX = mid.x;\n const currY = mid.y;\n const distance = touchDistance(event.touches);\n const deltaY = lastTouchScrollY - currY;\n const deltaX = lastTouchScrollX - currX;\n lastTouchScrollY = currY;\n lastTouchScrollX = currX;\n\n const totalMove = Math.hypot(currX - gestureStartMid.x, currY - gestureStartMid.y);\n const pinchDelta = Math.abs(distance - gestureStartDistance);\n const pinchRatio = gestureStartDistance > 0 ? distance / gestureStartDistance : 1;\n\n if (touchMode === 'undecided' || touchMode === 'gesture') {\n // Disambiguate between pinch-to-zoom and two-finger scroll\n if (pinchDelta > 20 && Math.abs(pinchRatio - 1) > 0.15 && pinchDelta > totalMove * 0.8) {\n touchMode = 'pinch';\n } else if (totalMove > 8) {\n touchMode = 'scroll';\n }\n }\n\n if (touchMode === 'pinch') {\n if (lastPinchDistance > 0 && Math.abs(distance - lastPinchDistance) > 1) {\n this.userZoomed = true;\n this.setZoom(this.zoom * (distance / lastPinchDistance), { x: currX, y: currY });\n }\n lastPinchDistance = distance;\n } else if (touchMode === 'scroll') {\n lastPinchDistance = distance;\n\n // A zoomed-in desktop pans; a fitted desktop turns the remote mouse wheel.\n if (this.canPan()) {\n this.panBy(deltaX * -1, deltaY * -1);\n pendingTouchWheelDelta = 0;\n return;\n }\n\n if (deltaY && pendingTouchWheelDelta\n && Math.sign(pendingTouchWheelDelta) !== Math.sign(deltaY)) {\n pendingTouchWheelDelta = 0;\n }\n pendingTouchWheelDelta += deltaY;\n\n while (Math.abs(pendingTouchWheelDelta) >= TOUCH_SCROLL_STEP) {\n const direction = Math.sign(pendingTouchWheelDelta);\n pendingTouchWheelDelta -= direction * TOUCH_SCROLL_STEP;\n const { x, y } = this.#toRemote({ clientX: currX, clientY: currY });\n this.client.sendWheel(direction, x, y);\n }\n }\n }\n },\n touchend: (event) => {\n if (!canSend()) return;\n event.preventDefault();\n\n if (event.touches.length === 0) {\n if (touchIsDown) {\n const { x, y } = this.#toRemote({ clientX: lastTouchX, clientY: lastTouchY });\n this.client.sendMouseButton(0, false, x, y);\n touchIsDown = false;\n }\n if (touchMode === 'pinch' && this.zoom <= 1.05) {\n this.resetZoom();\n }\n touchMode = null;\n pendingTouchWheelDelta = 0;\n lastPinchDistance = 0;\n } else if (event.touches.length === 1) {\n // A finger lifted mid-gesture leaves one behind that was never a tap;\n // letting it fall through would press the mouse where it happens to\n // rest. The gesture ends here and the stray finger is ignored.\n if (touchMode === 'gesture' || touchMode === 'pinch' || touchMode === 'scroll' || touchMode === 'undecided') {\n if (touchMode === 'pinch' && this.zoom <= 1.05) {\n this.resetZoom();\n }\n touchMode = null;\n lastPinchDistance = 0;\n pendingTouchWheelDelta = 0;\n }\n }\n },\n touchcancel: (event) => {\n if (!canSend()) return;\n if (touchIsDown) {\n const { x, y } = this.#toRemote({ clientX: lastTouchX, clientY: lastTouchY });\n this.client.sendMouseButton(0, false, x, y);\n touchIsDown = false;\n }\n if (touchMode === 'pinch' && this.zoom <= 1.05) {\n this.resetZoom();\n }\n touchMode = null;\n pendingTouchWheelDelta = 0;\n lastPinchDistance = 0;\n },\n keydown: (event) => {\n if (!canSend()) return;\n this.#syncKeyboardOnce(event);\n\n // Paste is the browser's to deliver: preventing the default here would\n // stop the paste event that carries the text the host is about to ask\n // for. The keystroke is sent from the paste handler instead.\n if (this.#isPasteShortcut(event)) return;\n\n const code = this.#translateCode(event.code);\n if (!this.client.sendKeyByCode(code, true)) return;\n event.preventDefault();\n\n // macOS withholds keyup while Command is held, which would leave the\n // key stuck down on the host. Releasing it at once is the only way to\n // keep the shortcut from repeating forever.\n if (IS_MAC && event.metaKey && !COMMAND_TO_CONTROL[event.code]) {\n this.client.sendKeyByCode(code, false);\n return;\n }\n\n this.heldKeys.add(code);\n },\n keyup: (event) => {\n if (!canSend()) return;\n if (this.#isPasteShortcut(event)) return;\n\n const code = this.#translateCode(event.code);\n if (this.client.sendKeyByCode(code, false)) {\n this.heldKeys.delete(code);\n event.preventDefault();\n }\n },\n paste: (event) => {\n if (!canSend()) return;\n\n /**\n * Only when the screen is what is being pasted into.\n *\n * This listener is on the document because a canvas cannot receive a\n * paste event of its own -- which means *every* paste in the page\n * arrives here, including one typed into the assistant's message box or\n * a password field. Without this guard the text went two places at\n * once: into the box the user was looking at, and into whatever window\n * happened to be open on the far machine.\n *\n * Focus is the test. The remote screen holds it when the remote screen\n * is what you are working in, and nothing else does at the same time.\n * (The terminal never had this problem because xterm stops the event\n * from propagating; a text field cannot be relied on to do that.)\n */\n const target = event.target;\n const onTheScreen = target instanceof Node && this.container.contains(target);\n if (document.activeElement !== this.canvas && !onTheScreen) return;\n\n event.preventDefault();\n const text = event.clipboardData?.getData('text/plain') || '';\n if (text) this.client.pasteToHost(text);\n },\n blur: () => this.releaseAllKeys(),\n };\n\n this.canvas.addEventListener('mousemove', this.handlers.mousemove);\n this.canvas.addEventListener('mousedown', this.handlers.mousedown);\n this.canvas.addEventListener('mouseup', this.handlers.mouseup);\n this.canvas.addEventListener('contextmenu', this.handlers.contextmenu);\n this.canvas.addEventListener('wheel', this.handlers.wheel, { passive: false });\n this.canvas.addEventListener('touchstart', this.handlers.touchstart, { passive: false });\n this.canvas.addEventListener('touchmove', this.handlers.touchmove, { passive: false });\n this.canvas.addEventListener('touchend', this.handlers.touchend, { passive: false });\n this.canvas.addEventListener('touchcancel', this.handlers.touchcancel, { passive: false });\n this.canvas.addEventListener('keydown', this.handlers.keydown);\n this.canvas.addEventListener('keyup', this.handlers.keyup);\n this.canvas.addEventListener('blur', this.handlers.blur);\n // A canvas is not editable, so the paste event lands on the document. The\n // handler checks that the canvas has focus before treating it as its own.\n document.addEventListener('paste', this.handlers.paste);\n }\n\n /** Command on a Mac stands in for Control everywhere the host is concerned. */\n #translateCode(code: number) {\n return IS_MAC ? COMMAND_TO_CONTROL[code] || code : code;\n }\n\n #isPasteShortcut(event) {\n if (event.code !== 'KeyV') return false;\n return IS_MAC ? event.metaKey : event.ctrlKey;\n }\n\n /** A key held while focus moves away would otherwise stay down on the host. */\n releaseAllKeys() {\n if (!this.client) return;\n for (const code of this.heldKeys) this.client.sendKeyByCode(code, false);\n this.heldKeys.clear();\n }\n\n focus() {\n this.canvas.focus();\n }\n\n blur() {\n this.canvas.blur();\n }\n\n destroy() {\n this.releaseAllKeys();\n window.removeEventListener('resize', this.onWindowResize);\n this.resizeObserver?.disconnect();\n\n if (this.handlers) {\n this.canvas.removeEventListener('mousemove', this.handlers.mousemove);\n this.canvas.removeEventListener('mousedown', this.handlers.mousedown);\n this.canvas.removeEventListener('mouseup', this.handlers.mouseup);\n this.canvas.removeEventListener('contextmenu', this.handlers.contextmenu);\n this.canvas.removeEventListener('wheel', this.handlers.wheel);\n this.canvas.removeEventListener('touchstart', this.handlers.touchstart);\n this.canvas.removeEventListener('touchmove', this.handlers.touchmove);\n this.canvas.removeEventListener('touchend', this.handlers.touchend);\n this.canvas.removeEventListener('touchcancel', this.handlers.touchcancel);\n this.canvas.removeEventListener('keydown', this.handlers.keydown);\n this.canvas.removeEventListener('keyup', this.handlers.keyup);\n this.canvas.removeEventListener('blur', this.handlers.blur);\n document.removeEventListener('paste', this.handlers.paste);\n }\n\n this.client = null;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,132 @@
1
+ import { Reader, Writer, concatBytes } from "./buffer";
2
+ import { parseServerCertificate } from "./cert";
3
+ const CS_CORE = 49153;
4
+ const CS_SECURITY = 49154;
5
+ const CS_NET = 49155;
6
+ const CS_CLUSTER = 49156;
7
+ const SC_CORE = 3073;
8
+ const SC_SECURITY = 3074;
9
+ const SC_NET = 3075;
10
+ const RDP_VERSION_5_PLUS = 524292;
11
+ const COLOR_16BPP = 51715;
12
+ const RNS_UD_SAS_DEL = 43523;
13
+ const SUPPORT_ERRINFO_PDU = 1;
14
+ const ENCRYPTION_LEVEL = {
15
+ NONE: 0,
16
+ LOW: 1,
17
+ CLIENT_COMPATIBLE: 2,
18
+ HIGH: 3,
19
+ FIPS: 4
20
+ };
21
+ function userDataBlock(type, body) {
22
+ const writer = new Writer(body.length + 4);
23
+ writer.u16le(type).u16le(body.length + 4).raw(body);
24
+ return writer.take();
25
+ }
26
+ function clientCoreData({ width, height, clientName, keyboardLayout, selectedProtocol }) {
27
+ const writer = new Writer(256);
28
+ writer.u32le(RDP_VERSION_5_PLUS);
29
+ writer.u16le(width);
30
+ writer.u16le(height);
31
+ writer.u16le(COLOR_16BPP);
32
+ writer.u16le(RNS_UD_SAS_DEL);
33
+ writer.u32le(keyboardLayout);
34
+ writer.u32le(2600);
35
+ writer.utf16le(clientName, 32);
36
+ writer.u32le(4);
37
+ writer.u32le(0);
38
+ writer.u32le(12);
39
+ writer.zeros(64);
40
+ writer.u16le(COLOR_16BPP);
41
+ writer.u16le(1);
42
+ writer.u32le(0);
43
+ writer.u16le(16);
44
+ writer.u16le(7);
45
+ writer.u16le(SUPPORT_ERRINFO_PDU);
46
+ writer.zeros(64);
47
+ writer.u8(0);
48
+ writer.u8(0);
49
+ writer.u32le(selectedProtocol);
50
+ return userDataBlock(CS_CORE, writer.take());
51
+ }
52
+ function clientSecurityData(encryptionMethods) {
53
+ const writer = new Writer(8);
54
+ writer.u32le(encryptionMethods);
55
+ writer.u32le(0);
56
+ return userDataBlock(CS_SECURITY, writer.take());
57
+ }
58
+ function clientClusterData() {
59
+ const writer = new Writer(8);
60
+ writer.u32le(13);
61
+ writer.u32le(0);
62
+ return userDataBlock(CS_CLUSTER, writer.take());
63
+ }
64
+ function clientNetworkData(channels) {
65
+ if (channels.length === 0) return new Uint8Array(0);
66
+ const writer = new Writer(4 + channels.length * 12);
67
+ writer.u32le(channels.length);
68
+ for (const channel of channels) {
69
+ const name = new Uint8Array(8);
70
+ for (let i = 0; i < Math.min(7, channel.name.length); i++) name[i] = channel.name.charCodeAt(i);
71
+ writer.raw(name);
72
+ writer.u32le(channel.options >>> 0);
73
+ }
74
+ return userDataBlock(CS_NET, writer.take());
75
+ }
76
+ function clientData(options) {
77
+ return concatBytes(
78
+ clientCoreData(options),
79
+ clientClusterData(),
80
+ clientSecurityData(options.encryptionMethods),
81
+ clientNetworkData(options.channels || [])
82
+ );
83
+ }
84
+ function parseServerData(blocks) {
85
+ const reader = new Reader(blocks);
86
+ const result = {
87
+ version: null,
88
+ encryptionMethod: 0,
89
+ encryptionLevel: ENCRYPTION_LEVEL.NONE,
90
+ serverRandom: null,
91
+ publicKey: null,
92
+ ioChannelId: 1003,
93
+ channelIds: []
94
+ };
95
+ while (reader.remaining >= 4) {
96
+ const type = reader.u16le();
97
+ const length = reader.u16le();
98
+ if (length < 4 || length - 4 > reader.remaining) break;
99
+ const body = new Reader(reader.raw(length - 4));
100
+ if (type === SC_CORE) {
101
+ result.version = body.remaining >= 4 ? body.u32le() : null;
102
+ continue;
103
+ }
104
+ if (type === SC_NET) {
105
+ result.ioChannelId = body.u16le();
106
+ const channelCount = body.u16le();
107
+ for (let i = 0; i < channelCount && body.remaining >= 2; i++) {
108
+ result.channelIds.push(body.u16le());
109
+ }
110
+ continue;
111
+ }
112
+ if (type === SC_SECURITY) {
113
+ result.encryptionMethod = body.u32le();
114
+ result.encryptionLevel = body.u32le();
115
+ if (result.encryptionMethod === 0 && result.encryptionLevel === 0) continue;
116
+ if (body.remaining < 8) continue;
117
+ const serverRandomLength = body.u32le();
118
+ const serverCertLength = body.u32le();
119
+ result.serverRandom = body.raw(serverRandomLength).slice();
120
+ result.publicKey = parseServerCertificate(body.raw(serverCertLength));
121
+ continue;
122
+ }
123
+ }
124
+ return result;
125
+ }
126
+ export {
127
+ ENCRYPTION_LEVEL,
128
+ RDP_VERSION_5_PLUS,
129
+ clientData,
130
+ parseServerData
131
+ };
132
+ //# sourceMappingURL=gcc.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/protocols/rdp/gcc.ts"],
4
+ "sourcesContent": ["// GCC user data: what the client says about itself in the MCS connect request,\n// and what the host answers with (MS-RDPBCGR 2.2.1.3 / 2.2.1.4).\n\nimport { Reader, Writer, concatBytes } from './buffer';\nimport { parseServerCertificate } from './cert';\n\nconst CS_CORE = 0xc001;\nconst CS_SECURITY = 0xc002;\nconst CS_NET = 0xc003;\nconst CS_CLUSTER = 0xc004;\n\nconst SC_CORE = 0x0c01;\nconst SC_SECURITY = 0x0c02;\nconst SC_NET = 0x0c03;\n\n/** RDP 5.0 and later. Anything newer would oblige us to speak newer PDUs. */\nexport const RDP_VERSION_5_PLUS = 0x00080004;\n\nconst COLOR_16BPP = 0xca03;\nconst RNS_UD_SAS_DEL = 0xaa03;\nconst SUPPORT_ERRINFO_PDU = 0x0001;\n\nexport const ENCRYPTION_LEVEL = {\n NONE: 0,\n LOW: 1,\n CLIENT_COMPATIBLE: 2,\n HIGH: 3,\n FIPS: 4,\n};\n\nfunction userDataBlock(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\n/**\n * TS_UD_CS_CORE. The desktop size asked for here is a request: the host answers\n * with the size it actually chose in its Demand Active PDU.\n */\nfunction clientCoreData({ width, height, clientName, keyboardLayout, selectedProtocol }) {\n const writer = new Writer(256);\n\n writer.u32le(RDP_VERSION_5_PLUS);\n writer.u16le(width);\n writer.u16le(height);\n writer.u16le(COLOR_16BPP);\n writer.u16le(RNS_UD_SAS_DEL);\n writer.u32le(keyboardLayout);\n writer.u32le(2600); // clientBuild\n writer.utf16le(clientName, 32); // clientName, 15 chars + NUL\n writer.u32le(4); // keyboardType: IBM enhanced\n writer.u32le(0); // keyboardSubType\n writer.u32le(12); // keyboardFunctionKey\n writer.zeros(64); // imeFileName\n writer.u16le(COLOR_16BPP); // postBeta2ColorDepth\n writer.u16le(1); // clientProductId\n writer.u32le(0); // serialNumber\n writer.u16le(16); // highColorDepth\n writer.u16le(0x0007); // supportedColorDepths: 15/16/24\n writer.u16le(SUPPORT_ERRINFO_PDU); // earlyCapabilityFlags\n writer.zeros(64); // clientDigProductId\n writer.u8(0); // connectionType: unknown\n writer.u8(0); // pad\n writer.u32le(selectedProtocol); // serverSelectedProtocol\n\n return userDataBlock(CS_CORE, writer.take());\n}\n\n/** TS_UD_CS_SEC. FIPS is deliberately absent -- this client does not do it. */\nfunction clientSecurityData(encryptionMethods: number) {\n const writer = new Writer(8);\n writer.u32le(encryptionMethods);\n writer.u32le(0); // extEncryptionMethods, French locale only\n return userDataBlock(CS_SECURITY, writer.take());\n}\n\n/** TS_UD_CS_CLUSTER, needed so the host reports redirection instead of hanging. */\nfunction clientClusterData() {\n const writer = new Writer(8);\n writer.u32le(0x0000000d); // REDIRECTION_SUPPORTED | redirection version 4\n writer.u32le(0); // RedirectedSessionID\n return userDataBlock(CS_CLUSTER, writer.take());\n}\n\nfunction clientNetworkData(channels) {\n if (channels.length === 0) return new Uint8Array(0);\n\n const writer = new Writer(4 + channels.length * 12);\n writer.u32le(channels.length);\n for (const channel of channels) {\n const name = new Uint8Array(8);\n for (let i = 0; i < Math.min(7, channel.name.length); i++) name[i] = channel.name.charCodeAt(i);\n writer.raw(name);\n writer.u32le(channel.options >>> 0);\n }\n return userDataBlock(CS_NET, writer.take());\n}\n\nexport function clientData(options) {\n return concatBytes(\n clientCoreData(options),\n clientClusterData(),\n clientSecurityData(options.encryptionMethods),\n clientNetworkData(options.channels || [])\n );\n}\n\n/**\n * Walk the host's answer. Only the security and network blocks carry anything\n * the client must act on; the core block is kept for logging.\n */\nexport function parseServerData(blocks) {\n const reader = new Reader(blocks);\n const result = {\n version: null,\n encryptionMethod: 0,\n encryptionLevel: ENCRYPTION_LEVEL.NONE,\n serverRandom: null,\n publicKey: null,\n ioChannelId: 1003,\n channelIds: [],\n };\n\n while (reader.remaining >= 4) {\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\n if (type === SC_CORE) {\n result.version = body.remaining >= 4 ? body.u32le() : null;\n continue;\n }\n\n if (type === SC_NET) {\n result.ioChannelId = body.u16le();\n const channelCount = body.u16le();\n for (let i = 0; i < channelCount && body.remaining >= 2; i++) {\n result.channelIds.push(body.u16le());\n }\n continue;\n }\n\n if (type === SC_SECURITY) {\n result.encryptionMethod = body.u32le();\n result.encryptionLevel = body.u32le();\n\n // With TLS in play the host reports no RDP-level encryption and stops.\n if (result.encryptionMethod === 0 && result.encryptionLevel === 0) continue;\n if (body.remaining < 8) continue;\n\n const serverRandomLength = body.u32le();\n const serverCertLength = body.u32le();\n result.serverRandom = body.raw(serverRandomLength).slice();\n result.publicKey = parseServerCertificate(body.raw(serverCertLength));\n continue;\n }\n }\n\n return result;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,132 @@
1
+ const SCANCODES = {
2
+ Escape: 1,
3
+ Digit1: 2,
4
+ Digit2: 3,
5
+ Digit3: 4,
6
+ Digit4: 5,
7
+ Digit5: 6,
8
+ Digit6: 7,
9
+ Digit7: 8,
10
+ Digit8: 9,
11
+ Digit9: 10,
12
+ Digit0: 11,
13
+ Minus: 12,
14
+ Equal: 13,
15
+ Backspace: 14,
16
+ Tab: 15,
17
+ KeyQ: 16,
18
+ KeyW: 17,
19
+ KeyE: 18,
20
+ KeyR: 19,
21
+ KeyT: 20,
22
+ KeyY: 21,
23
+ KeyU: 22,
24
+ KeyI: 23,
25
+ KeyO: 24,
26
+ KeyP: 25,
27
+ BracketLeft: 26,
28
+ BracketRight: 27,
29
+ Enter: 28,
30
+ ControlLeft: 29,
31
+ KeyA: 30,
32
+ KeyS: 31,
33
+ KeyD: 32,
34
+ KeyF: 33,
35
+ KeyG: 34,
36
+ KeyH: 35,
37
+ KeyJ: 36,
38
+ KeyK: 37,
39
+ KeyL: 38,
40
+ Semicolon: 39,
41
+ Quote: 40,
42
+ Backquote: 41,
43
+ ShiftLeft: 42,
44
+ Backslash: 43,
45
+ KeyZ: 44,
46
+ KeyX: 45,
47
+ KeyC: 46,
48
+ KeyV: 47,
49
+ KeyB: 48,
50
+ KeyN: 49,
51
+ KeyM: 50,
52
+ Comma: 51,
53
+ Period: 52,
54
+ Slash: 53,
55
+ ShiftRight: 54,
56
+ NumpadMultiply: 55,
57
+ AltLeft: 56,
58
+ Space: 57,
59
+ CapsLock: 58,
60
+ F1: 59,
61
+ F2: 60,
62
+ F3: 61,
63
+ F4: 62,
64
+ F5: 63,
65
+ F6: 64,
66
+ F7: 65,
67
+ F8: 66,
68
+ F9: 67,
69
+ F10: 68,
70
+ NumLock: 69,
71
+ ScrollLock: 70,
72
+ Numpad7: 71,
73
+ Numpad8: 72,
74
+ Numpad9: 73,
75
+ NumpadSubtract: 74,
76
+ Numpad4: 75,
77
+ Numpad5: 76,
78
+ Numpad6: 77,
79
+ NumpadAdd: 78,
80
+ Numpad1: 79,
81
+ Numpad2: 80,
82
+ Numpad3: 81,
83
+ Numpad0: 82,
84
+ NumpadDecimal: 83,
85
+ IntlBackslash: 86,
86
+ F11: 87,
87
+ F12: 88,
88
+ IntlRo: 115,
89
+ IntlYen: 125
90
+ };
91
+ const EXTENDED_SCANCODES = {
92
+ NumpadEnter: 28,
93
+ ControlRight: 29,
94
+ NumpadDivide: 53,
95
+ PrintScreen: 55,
96
+ AltRight: 56,
97
+ Home: 71,
98
+ ArrowUp: 72,
99
+ PageUp: 73,
100
+ ArrowLeft: 75,
101
+ ArrowRight: 77,
102
+ End: 79,
103
+ ArrowDown: 80,
104
+ PageDown: 81,
105
+ Insert: 82,
106
+ Delete: 83,
107
+ MetaLeft: 91,
108
+ MetaRight: 92,
109
+ ContextMenu: 93
110
+ };
111
+ function lookupScancode(code) {
112
+ if (Object.prototype.hasOwnProperty.call(SCANCODES, code)) {
113
+ return { scancode: SCANCODES[code], extended: false };
114
+ }
115
+ if (Object.prototype.hasOwnProperty.call(EXTENDED_SCANCODES, code)) {
116
+ return { scancode: EXTENDED_SCANCODES[code], extended: true };
117
+ }
118
+ return null;
119
+ }
120
+ const CTRL_ALT_DEL = [
121
+ { code: "ControlLeft", pressed: true },
122
+ { code: "AltLeft", pressed: true },
123
+ { code: "Delete", pressed: true },
124
+ { code: "Delete", pressed: false },
125
+ { code: "AltLeft", pressed: false },
126
+ { code: "ControlLeft", pressed: false }
127
+ ];
128
+ export {
129
+ CTRL_ALT_DEL,
130
+ lookupScancode
131
+ };
132
+ //# sourceMappingURL=keymap.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/protocols/rdp/keymap.ts"],
4
+ "sourcesContent": ["// KeyboardEvent.code -> PC/AT set 1 scancode, which is what RDP carries.\n// Keys marked extended need KBDFLAGS_EXTENDED so the host tells, for example,\n// the numeric keypad Enter from the main one.\n\nconst SCANCODES = {\n Escape: 0x01,\n Digit1: 0x02, Digit2: 0x03, Digit3: 0x04, Digit4: 0x05, Digit5: 0x06,\n Digit6: 0x07, Digit7: 0x08, Digit8: 0x09, Digit9: 0x0a, Digit0: 0x0b,\n Minus: 0x0c, Equal: 0x0d, Backspace: 0x0e, Tab: 0x0f,\n KeyQ: 0x10, KeyW: 0x11, KeyE: 0x12, KeyR: 0x13, KeyT: 0x14,\n KeyY: 0x15, KeyU: 0x16, KeyI: 0x17, KeyO: 0x18, KeyP: 0x19,\n BracketLeft: 0x1a, BracketRight: 0x1b, Enter: 0x1c, ControlLeft: 0x1d,\n KeyA: 0x1e, KeyS: 0x1f, KeyD: 0x20, KeyF: 0x21, KeyG: 0x22,\n KeyH: 0x23, KeyJ: 0x24, KeyK: 0x25, KeyL: 0x26,\n Semicolon: 0x27, Quote: 0x28, Backquote: 0x29, ShiftLeft: 0x2a, Backslash: 0x2b,\n KeyZ: 0x2c, KeyX: 0x2d, KeyC: 0x2e, KeyV: 0x2f, KeyB: 0x30,\n KeyN: 0x31, KeyM: 0x32, Comma: 0x33, Period: 0x34, Slash: 0x35,\n ShiftRight: 0x36, NumpadMultiply: 0x37, AltLeft: 0x38, Space: 0x39, CapsLock: 0x3a,\n F1: 0x3b, F2: 0x3c, F3: 0x3d, F4: 0x3e, F5: 0x3f,\n F6: 0x40, F7: 0x41, F8: 0x42, F9: 0x43, F10: 0x44,\n NumLock: 0x45, ScrollLock: 0x46,\n Numpad7: 0x47, Numpad8: 0x48, Numpad9: 0x49, NumpadSubtract: 0x4a,\n Numpad4: 0x4b, Numpad5: 0x4c, Numpad6: 0x4d, NumpadAdd: 0x4e,\n Numpad1: 0x4f, Numpad2: 0x50, Numpad3: 0x51, Numpad0: 0x52, NumpadDecimal: 0x53,\n IntlBackslash: 0x56, F11: 0x57, F12: 0x58,\n IntlRo: 0x73, IntlYen: 0x7d,\n};\n\nconst EXTENDED_SCANCODES = {\n NumpadEnter: 0x1c,\n ControlRight: 0x1d,\n NumpadDivide: 0x35,\n PrintScreen: 0x37,\n AltRight: 0x38,\n Home: 0x47,\n ArrowUp: 0x48,\n PageUp: 0x49,\n ArrowLeft: 0x4b,\n ArrowRight: 0x4d,\n End: 0x4f,\n ArrowDown: 0x50,\n PageDown: 0x51,\n Insert: 0x52,\n Delete: 0x53,\n MetaLeft: 0x5b,\n MetaRight: 0x5c,\n ContextMenu: 0x5d,\n};\n\n/** Returns { scancode, extended } or null when the key has no RDP equivalent. */\nexport function lookupScancode(code: string) {\n if (Object.prototype.hasOwnProperty.call(SCANCODES, code)) {\n return { scancode: SCANCODES[code], extended: false };\n }\n if (Object.prototype.hasOwnProperty.call(EXTENDED_SCANCODES, code)) {\n return { scancode: EXTENDED_SCANCODES[code], extended: true };\n }\n return null;\n}\n\nexport const CTRL_ALT_DEL = [\n { code: 'ControlLeft', pressed: true },\n { code: 'AltLeft', pressed: true },\n { code: 'Delete', pressed: true },\n { code: 'Delete', pressed: false },\n { code: 'AltLeft', pressed: false },\n { code: 'ControlLeft', pressed: false },\n];\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,223 @@
1
+ import { Reader, Writer, concatBytes } from "./buffer";
2
+ import { dataFrame, dataPayload } from "./x224";
3
+ const MCS_BASE_CHANNEL_ID = 1001;
4
+ const MCS_GLOBAL_CHANNEL_ID = 1003;
5
+ const DOMAIN_MCSPDU = {
6
+ ERECT_DOMAIN_REQUEST: 1,
7
+ DISCONNECT_PROVIDER_ULTIMATUM: 8,
8
+ ATTACH_USER_REQUEST: 10,
9
+ ATTACH_USER_CONFIRM: 11,
10
+ CHANNEL_JOIN_REQUEST: 14,
11
+ CHANNEL_JOIN_CONFIRM: 15,
12
+ SEND_DATA_REQUEST: 25,
13
+ SEND_DATA_INDICATION: 26
14
+ };
15
+ const DISCONNECT_REASONS = {
16
+ 0: "The host closed the RDP session (domain disconnected).",
17
+ 1: "The host closed the RDP session (provider initiated).",
18
+ 2: "The host closed the RDP session (token purged).",
19
+ 3: "The host closed the RDP session (user requested).",
20
+ 4: "The host closed the RDP session (channel purged)."
21
+ };
22
+ function berLength(length) {
23
+ if (length > 255) return new Uint8Array([130, length >> 8 & 255, length & 255]);
24
+ if (length > 127) return new Uint8Array([129, length]);
25
+ return new Uint8Array([length]);
26
+ }
27
+ function berTlv(tag, content) {
28
+ return concatBytes(new Uint8Array(tag), berLength(content.length), content);
29
+ }
30
+ function berInteger(value) {
31
+ let body;
32
+ if (value < 128) body = new Uint8Array([value]);
33
+ else if (value < 32768) body = new Uint8Array([value >> 8, value & 255]);
34
+ else if (value < 8388608) body = new Uint8Array([value >> 16, value >> 8 & 255, value & 255]);
35
+ else body = new Uint8Array([value >>> 24, value >> 16 & 255, value >> 8 & 255, value & 255]);
36
+ return berTlv([2], body);
37
+ }
38
+ const berOctetString = (bytes) => berTlv([4], bytes);
39
+ const berBoolean = (value) => berTlv([1], new Uint8Array([value ? 255 : 0]));
40
+ function berDomainParameters(values) {
41
+ return berTlv([48], concatBytes(...values.map(berInteger)));
42
+ }
43
+ function perLength(writer, length) {
44
+ if (length > 127) writer.u16be(32768 | length);
45
+ else writer.u8(length);
46
+ return writer;
47
+ }
48
+ function perReadLength(reader) {
49
+ const first = reader.u8();
50
+ return first & 128 ? (first & 127) << 8 | reader.u8() : first;
51
+ }
52
+ const T124_OBJECT_IDENTIFIER = new Uint8Array([5, 0, 20, 124, 0, 1]);
53
+ const H221_CLIENT_KEY = new Uint8Array([68, 117, 99, 97]);
54
+ const H221_SERVER_KEY = new Uint8Array([77, 99, 68, 110]);
55
+ function gccConferenceCreateRequest(clientData) {
56
+ const writer = new Writer(clientData.length + 32);
57
+ writer.u8(0);
58
+ writer.raw(T124_OBJECT_IDENTIFIER);
59
+ perLength(writer, clientData.length + 14);
60
+ writer.u8(0);
61
+ writer.u8(8);
62
+ writer.u8(0);
63
+ writer.u8(16);
64
+ writer.u8(0);
65
+ writer.u8(1);
66
+ writer.u8(192);
67
+ writer.u8(0);
68
+ writer.raw(H221_CLIENT_KEY);
69
+ perLength(writer, clientData.length);
70
+ writer.raw(clientData);
71
+ return writer.take();
72
+ }
73
+ function gccServerDataBlocks(userData) {
74
+ for (let i = 0; i + 4 <= userData.length; i++) {
75
+ if (userData[i] === H221_SERVER_KEY[0] && userData[i + 1] === H221_SERVER_KEY[1] && userData[i + 2] === H221_SERVER_KEY[2] && userData[i + 3] === H221_SERVER_KEY[3]) {
76
+ const reader = new Reader(userData, i + 4);
77
+ const length = perReadLength(reader);
78
+ const blocks = reader.rest();
79
+ return blocks.subarray(0, Math.min(length, blocks.length));
80
+ }
81
+ }
82
+ throw new Error("RDP: the host response contained no GCC server data");
83
+ }
84
+ const DOMAIN_TARGET = [34, 2, 0, 1, 0, 1, 65535, 2];
85
+ const DOMAIN_MINIMUM = [1, 1, 1, 1, 0, 1, 1056, 2];
86
+ const DOMAIN_MAXIMUM = [65535, 64535, 65535, 1, 0, 1, 65535, 2];
87
+ function connectInitial(clientData) {
88
+ const userData = gccConferenceCreateRequest(clientData);
89
+ const content = concatBytes(
90
+ berOctetString(new Uint8Array([1])),
91
+ // callingDomainSelector
92
+ berOctetString(new Uint8Array([1])),
93
+ // calledDomainSelector
94
+ berBoolean(true),
95
+ // upwardFlag
96
+ berDomainParameters(DOMAIN_TARGET),
97
+ berDomainParameters(DOMAIN_MINIMUM),
98
+ berDomainParameters(DOMAIN_MAXIMUM),
99
+ berOctetString(userData)
100
+ );
101
+ return dataFrame(berTlv([127, 101], content));
102
+ }
103
+ function readBerTlv(reader) {
104
+ let tag = reader.u8();
105
+ if ((tag & 31) === 31) tag = tag << 8 | reader.u8();
106
+ let length = reader.u8();
107
+ if (length & 128) {
108
+ const byteCount = length & 127;
109
+ length = 0;
110
+ for (let i = 0; i < byteCount; i++) length = length << 8 | reader.u8();
111
+ }
112
+ return { tag, value: reader.raw(length) };
113
+ }
114
+ function parseConnectResponse(frame) {
115
+ const reader = new Reader(dataPayload(frame));
116
+ const outer = readBerTlv(reader);
117
+ if (outer.tag !== 32614) {
118
+ throw new Error(`RDP: expected an MCS connect response, got BER tag 0x${outer.tag.toString(16)}`);
119
+ }
120
+ const inner = new Reader(outer.value);
121
+ const result = readBerTlv(inner);
122
+ readBerTlv(inner);
123
+ readBerTlv(inner);
124
+ const userData = readBerTlv(inner);
125
+ if (result.value[0] !== 0) {
126
+ throw new Error(`RDP: the host rejected the MCS connection (result ${result.value[0]})`);
127
+ }
128
+ return gccServerDataBlocks(userData.value);
129
+ }
130
+ const domainHeader = (pdu) => pdu << 2;
131
+ function erectDomainRequest() {
132
+ const writer = new Writer(8);
133
+ writer.u8(domainHeader(DOMAIN_MCSPDU.ERECT_DOMAIN_REQUEST));
134
+ writer.u8(1).u8(0);
135
+ writer.u8(1).u8(0);
136
+ return dataFrame(writer.take());
137
+ }
138
+ function attachUserRequest() {
139
+ return dataFrame(new Uint8Array([domainHeader(DOMAIN_MCSPDU.ATTACH_USER_REQUEST)]));
140
+ }
141
+ function parseAttachUserConfirm(frame) {
142
+ const reader = new Reader(dataPayload(frame));
143
+ const header = reader.u8();
144
+ if (header >> 2 !== DOMAIN_MCSPDU.ATTACH_USER_CONFIRM) {
145
+ throw new Error(`RDP: expected an MCS attach-user confirm, got PDU ${header >> 2}`);
146
+ }
147
+ const result = reader.u8();
148
+ if (result !== 0) throw new Error(`RDP: the host refused to attach a user (result ${result})`);
149
+ const userId = header & 2 ? reader.u16be() + MCS_BASE_CHANNEL_ID : MCS_BASE_CHANNEL_ID;
150
+ return { userId };
151
+ }
152
+ function channelJoinRequest(userId, channelId) {
153
+ const writer = new Writer(8);
154
+ writer.u8(domainHeader(DOMAIN_MCSPDU.CHANNEL_JOIN_REQUEST));
155
+ writer.u16be(userId - MCS_BASE_CHANNEL_ID);
156
+ writer.u16be(channelId);
157
+ return dataFrame(writer.take());
158
+ }
159
+ function parseChannelJoinConfirm(frame) {
160
+ const reader = new Reader(dataPayload(frame));
161
+ const header = reader.u8();
162
+ if (header >> 2 !== DOMAIN_MCSPDU.CHANNEL_JOIN_CONFIRM) {
163
+ throw new Error(`RDP: expected an MCS channel-join confirm, got PDU ${header >> 2}`);
164
+ }
165
+ const result = reader.u8();
166
+ reader.u16be();
167
+ const requested = reader.u16be();
168
+ if (result !== 0) {
169
+ throw new Error(`RDP: the host refused to join channel ${requested} (result ${result})`);
170
+ }
171
+ const channelId = reader.remaining >= 2 ? reader.u16be() : requested;
172
+ return { channelId: channelId || requested };
173
+ }
174
+ function sendDataRequest(userId, channelId, payload) {
175
+ const writer = new Writer(payload.length + 16);
176
+ writer.u8(domainHeader(DOMAIN_MCSPDU.SEND_DATA_REQUEST));
177
+ writer.u16be(userId - MCS_BASE_CHANNEL_ID);
178
+ writer.u16be(channelId);
179
+ writer.u8(112);
180
+ perLength(writer, payload.length);
181
+ writer.raw(payload);
182
+ return dataFrame(writer.take());
183
+ }
184
+ function parseDomainPdu(frame) {
185
+ const reader = new Reader(dataPayload(frame));
186
+ const header = reader.u8();
187
+ const type = header >> 2;
188
+ if (type === DOMAIN_MCSPDU.SEND_DATA_INDICATION) {
189
+ reader.u16be();
190
+ const channelId = reader.u16be();
191
+ reader.u8();
192
+ const length = perReadLength(reader);
193
+ const payload = reader.rest();
194
+ return {
195
+ type: "send-data",
196
+ channelId,
197
+ payload: payload.subarray(0, Math.min(length, payload.length))
198
+ };
199
+ }
200
+ if (type === DOMAIN_MCSPDU.DISCONNECT_PROVIDER_ULTIMATUM) {
201
+ const reason = (header & 1) << 1 | (reader.remaining > 0 ? reader.u8() >> 7 : 0);
202
+ return {
203
+ type: "disconnect",
204
+ reason,
205
+ message: DISCONNECT_REASONS[reason] || `The host closed the RDP session (reason ${reason}).`
206
+ };
207
+ }
208
+ return { type: "other", mcsPduType: type };
209
+ }
210
+ export {
211
+ MCS_BASE_CHANNEL_ID,
212
+ MCS_GLOBAL_CHANNEL_ID,
213
+ attachUserRequest,
214
+ channelJoinRequest,
215
+ connectInitial,
216
+ erectDomainRequest,
217
+ parseAttachUserConfirm,
218
+ parseChannelJoinConfirm,
219
+ parseConnectResponse,
220
+ parseDomainPdu,
221
+ sendDataRequest
222
+ };
223
+ //# sourceMappingURL=mcs.js.map