60 lines
1.8 KiB
JavaScript
60 lines
1.8 KiB
JavaScript
// Minimal fake DSH for the socat-WS PoC: plain HTTP on 127.0.0.1:8080 plus a
|
|||
|
|
// WebSocket echo (handshake + one text-frame round-trip) using only node built-ins.
|
||
|
|
import { createServer } from 'node:http'
|
||
|
|
import { createHash } from 'node:crypto'
|
||
|
|
|
||
|
|
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
||
|
|
const acceptKey = (key) => createHash('sha1').update(key + GUID).digest('base64')
|
||
|
|
|
||
|
|
const server = createServer((req, res) => {
|
||
|
|
res.writeHead(200, { 'Content-Type': 'text/plain' })
|
||
|
|
res.end('fake-dsh\n')
|
||
|
|
})
|
||
|
|
|
||
|
|
server.on('upgrade', (req, socket) => {
|
||
|
|
const key = req.headers['sec-websocket-key']
|
||
|
|
if (!key) {
|
||
|
|
socket.destroy()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
socket.write(
|
||
|
|
'HTTP/1.1 101 Switching Protocols\r\n' +
|
||
|
|
'Upgrade: websocket\r\n' +
|
||
|
|
'Connection: Upgrade\r\n' +
|
||
|
|
`Sec-WebSocket-Accept: ${acceptKey(key)}\r\n\r\n`,
|
||
|
|
)
|
||
|
|
socket.on('data', (buf) => {
|
||
|
|
const opcode = buf[0] & 0x0f
|
||
|
|
if (opcode === 0x8) {
|
||
|
|
socket.end()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (opcode !== 0x1 && opcode !== 0x2) return
|
||
|
|
const masked = (buf[1] & 0x80) !== 0
|
||
|
|
let len = buf[1] & 0x7f
|
||
|
|
let offset = 2
|
||
|
|
if (len === 126) {
|
||
|
|
len = buf.readUInt16BE(2)
|
||
|
|
offset = 4
|
||
|
|
} else if (len === 127) {
|
||
|
|
return // not exercised in the PoC
|
||
|
|
}
|
||
|
|
let maskKey
|
||
|
|
if (masked) {
|
||
|
|
maskKey = buf.subarray(offset, offset + 4)
|
||
|
|
offset += 4
|
||
|
|
}
|
||
|
|
const payload = Buffer.from(buf.subarray(offset, offset + len))
|
||
|
|
if (masked && maskKey) {
|
||
|
|
for (let i = 0; i < payload.length; i++) payload[i] ^= maskKey[i % 4]
|
||
|
|
}
|
||
|
|
// Echo back as an unmasked text frame.
|
||
|
|
const out = Buffer.alloc(2 + payload.length)
|
||
|
|
out[0] = 0x81
|
||
|
|
out[1] = payload.length
|
||
|
|
payload.copy(out, 2)
|
||
|
|
socket.write(out)
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
server.listen(8080, '127.0.0.1', () => console.log('fake-dsh listening on 127.0.0.1:8080'))
|