Created
January 3, 2024 20:38
-
-
Save truemedian/96fd21e5d055f6b09ad50bf094385ced to your computer and use it in GitHub Desktop.
This file contains 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
local pack, unpack, char, byte = string.pack, string.unpack, string.char, string.byte | |
local random = math.random | |
local leftover_fmt = {'>I1', '>I2', '>I3'} | |
local function mask_message(data, mask) | |
local res = {} | |
local len = #data | |
local left = len & 3; | |
local j = 1 | |
for i = 1, len - 3, 4 do | |
res[j] = pack('>I4', unpack('>I4', data, i) ~ mask) | |
j = j + 1 | |
end | |
if left ~= 0 then | |
local leftover_mask = mask >> (8 * (4 - left)) | |
res[j] = pack(leftover_fmt[left], unpack(leftover_fmt[left], data, len - left + 1) ~ leftover_mask) | |
end | |
return table.concat(res) | |
end | |
-- RAND_MAX has a minimum value of 0x7fff, so assume that's what we have | |
local function rand32() | |
return random(0x1000, 0xffff) << 16 | random(0x1000, 0xffff) | |
end | |
local function encode(opcode, payload, options) | |
options = options or {} | |
local msg = {} | |
msg[1] = opcode | |
msg[1] = msg[1] | (options.fin ~= false and 0x80 or 0) -- fin is true by default | |
msg[1] = msg[1] | (options.rsv1 == true and 0x40 or 0) | |
msg[1] = msg[1] | (options.rsv2 == true and 0x20 or 0) | |
msg[1] = msg[1] | (options.rsv3 == true and 0x10 or 0) | |
msg[1] = char(msg[1]) | |
local len = #payload | |
if len < 126 then | |
msg[2] = char(len | (options.mask and 0x80 or 0)) | |
elseif len < 0x10000 then | |
msg[2] = pack('>BI4', 126 | (options.mask and 0x80 or 0), len) | |
else | |
msg[2] = pack('>BI8', 127 | (options.mask and 0x80 or 0), len) | |
end | |
if options.mask then | |
local mask = rand32() | |
msg[3] = pack('>I4', mask) | |
msg[4] = mask_message(payload, mask) | |
else | |
msg[3] = payload | |
end | |
return table.concat(msg) | |
end | |
local function decode(message) | |
local options = {} | |
local opcode = byte(message, 1) & 0xf | |
options.fin = byte(message, 1) & 0x80 ~= 0 | |
options.rsv1 = byte(message, 1) & 0x40 ~= 0 | |
options.rsv2 = byte(message, 1) & 0x20 ~= 0 | |
options.rsv3 = byte(message, 1) & 0x10 ~= 0 | |
local len = byte(message, 2) & 0x7f | |
local offset = 3 | |
if len == 126 then | |
len = unpack('>I2', message, offset) | |
offset = 5 | |
elseif len == 127 then | |
len = unpack('>I8', message, offset) | |
offset = 11 | |
end | |
if byte(message, 2) & 0x80 ~= 0 then | |
options.mask = unpack('>I4', message, offset) | |
offset = offset + 4 | |
end | |
local payload = message:sub(offset, offset + len - 1) | |
if options.mask then | |
payload = mask_message(payload, options.mask) | |
end | |
return opcode, payload, options | |
end | |
local encoded = encode(0x1, 'Hello, world!', {mask = true}) | |
local op, decoded, options = decode(encoded) | |
print('opcode: ' .. op) | |
print('payload: ' .. decoded) | |
print('fin: ' .. tostring(options.fin)) | |
print('rsv1: ' .. tostring(options.rsv1)) | |
print('rsv2: ' .. tostring(options.rsv2)) | |
print('rsv3: ' .. tostring(options.rsv3)) | |
print('mask: ' .. tostring(options.mask)) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment