Created
September 13, 2021 23:15
-
-
Save prettydiff/ce40b6509f72a5fadfc6588b76b76ce8 to your computer and use it in GitHub Desktop.
websocket decode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unmask = function terminal_commands_websocket_unmask(socket:socketClient, data:Buffer):Uint8Array { | |
// reference - https://cookie.engineer/weblog/articles/implementers-guide-to-websockets.html | |
if (socket.fragment === null) { | |
socket.fragment = { | |
opcode: 0x00, | |
payload: Buffer.alloc(0) | |
}; | |
} | |
if (data.length < 3) { | |
return null; | |
} | |
const chunk:socketChunk = { | |
close: false, | |
fragment: false, | |
payload: null, | |
response: null | |
}, | |
fragment:socketFragment = socket.fragment, | |
opcode:number = (data[0] & 15), | |
opLabel:string = (function terminal_commands_websocket_unmask_opLabel():string { | |
if (opcode === 0x00) { | |
return "continuation"; | |
} | |
if (opcode === 0x01) { | |
return "text"; | |
} | |
if (opcode === 0x02) { | |
return "binary"; | |
} | |
if (opcode === 0x08) { | |
return "close"; | |
} | |
if (opcode === 0x09) { | |
return "ping"; | |
} | |
if (opcode === 0x0a) { | |
return "pong"; | |
} | |
return "unsupported"; | |
}()), | |
finFlag:boolean = ((data[0] & 128) === 128), | |
maskFlag:boolean = ((data[1] & 128) === 128), | |
frames:socketFrame = { | |
binary: function terminal_commands_websocket_unmask_binary():void { | |
// 0x01: Text Frame (possibly fragmented) | |
// 0x02: Binary Frame (possibly fragmented) | |
if (finFlag === true) { | |
chunk.payload = payload; | |
} else if (payload !== null) { | |
const payloadPartial:Buffer = Buffer.alloc(fragment.payload.length + dataLength); | |
fragment.payload.copy(payloadPartial, 0); | |
payload.set(payloadPartial, fragment.payload.length); | |
fragment.payload = payloadPartial; | |
fragment.opcode = opcode; | |
} | |
}, | |
close: function terminal_commands_websocket_unmask_close(code:1000|1002):void { | |
// 0x08: Connection Close Frame | |
const buffer:Buffer = Buffer.alloc(4); | |
// close status codes (RFC 6455, 7.4.1) | |
// * 1000 (normal) normal closure | |
// * 1001 (normal) going away | |
// * 1002 (normal) protocol error | |
// * 1015 (normal, only server) TLS encryption error | |
// | |
// * 1003 (exotic) terminate connection due to data error (e.g. only text frame supported, but binary frame received) | |
// * 1007 (exotic) data inconsistency (e.g. no `utf8` encoded text frame) | |
// * 1008 (exotic) policy violation | |
// * 1009 (exotic) message too big to process | |
// * 1010 (exotic, only client) terminate connection because server did not confirm extensions | |
// * 1011 (exotic, only server) unexpected error | |
// | |
// * 1004-1006 reserved for future use (DO NOT USE) | |
buffer[0] = 128 + 0x08; // close | |
buffer[1] = 0 + 0x02; // unmasked (client and server) | |
buffer[1] = (code >> 8) & 0xff; | |
buffer[2] = (code >> 0) & 0xff; | |
chunk.close = true; | |
chunk.response = buffer; | |
}, | |
continuation: function terminal_commands_websocket_unmask_continuation():void { | |
// 0x00: Continuation Frame | |
if (payload !== null) { | |
const payloadPartial:Buffer = Buffer.alloc(fragment.payload.length + dataLength); | |
fragment.payload.copy(payload, 0); | |
payload.set(payloadPartial, fragment.payload.length); | |
fragment.payload = payloadPartial; | |
} | |
if (finFlag === true) { | |
chunk.payload = fragment.payload; | |
fragment.opcode = 0x00; | |
fragment.payload = Buffer.alloc(0); | |
} | |
}, | |
ping: function terminal_commands_websocket_unmask_ping():void { | |
// 0x09: Ping Frame | |
const buffer:Buffer = Buffer.alloc(2 + (payload === null ? 0 : dataLength)); | |
buffer[0] = 128 + 0x0a; // fin, pong | |
buffer[1] = 0 + 0x00; // unmasked | |
if (payload !== null) { | |
buffer.write(payload.toString(), 2); | |
} | |
chunk.response = buffer; | |
}, | |
pong: function terminal_commands_websocket_unmask_pong():void { | |
// 0x0a: Pong Frame | |
chunk.fragment = true; | |
}, | |
}; | |
let dataLength:number = data[1] & 127, | |
mask:Buffer = Buffer.alloc(4), | |
payload:Uint8Array = null; | |
if (dataLength < 126) { | |
// 7 bit payload | |
if (maskFlag === true) { | |
mask = data.slice(2, 6); | |
payload = data.slice(6, 6 + dataLength); | |
} else { | |
mask = null; | |
payload = data.slice(2, 2 + dataLength); | |
} | |
} else if (dataLength === 126) { | |
// 16 bit payload | |
dataLength = (data[2] << 8) + data[3]; | |
if (dataLength > data.length) { | |
chunk.fragment = true; | |
return null; | |
} | |
if (maskFlag === true) { | |
mask = data.slice(4, 8); | |
payload = data.slice(8, 8 + dataLength); | |
} else { | |
mask = null; | |
payload = data.slice(4, 4 + dataLength); | |
} | |
} else { | |
// 64 bit payload | |
const hi = (data[2] * 0x1000000) + ((data[3] << 16) | (data[4] << 8) | data[5]), | |
lo = (data[6] * 0x1000000) + ((data[7] << 16) | (data[8] << 8) | data[9]); | |
dataLength = (hi * 4294967296) + lo; | |
if (dataLength > data.length) { | |
chunk.fragment = true; | |
return null; | |
} | |
if (maskFlag === true) { | |
mask = data.slice(10, 14); | |
payload = data.slice(14, 14 + dataLength); | |
} else { | |
mask = null; | |
payload = data.slice(10, 10 + dataLength); | |
} | |
} | |
if (mask !== null) { | |
payload = payload.map((value, index) => value ^ mask[index % 4]); | |
} | |
if (opcode === 0x00) { | |
frames.continuation(); | |
} else if (opcode === 0x01 || opcode === 0x02) { | |
frames.binary(); | |
} else if (opcode === 0x08) { | |
frames.close(1000); | |
} else if (opcode === 0x09) { | |
frames.ping(); | |
} else if (opcode === 0x0a) { | |
frames.pong(); | |
} else { | |
frames.close(1002); | |
} | |
if (chunk.response !== null) { | |
socket.write(chunk.response); | |
if (chunk.close === true) { | |
socket.end(); | |
} | |
} | |
return chunk.payload; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment