You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lua-lib/websocket/tools.lua

203 lines
4.6 KiB

local bit = require'websocket.bit'
local mime = require'mime'
local rol = bit.rol
local bxor = bit.bxor
local bor = bit.bor
local band = bit.band
local bnot = bit.bnot
local lshift = bit.lshift
local rshift = bit.rshift
local sunpack = string.unpack
local srep = string.rep
local schar = string.char
local tremove = table.remove
local tinsert = table.insert
local tconcat = table.concat
local mrandom = math.random
local read_n_bytes = function(str, pos, n)
pos = pos or 1
return pos+n, string.byte(str, pos, pos + n - 1)
end
local read_int8 = function(str, pos)
return read_n_bytes(str, pos, 1)
end
local read_int16 = function(str, pos)
local new_pos,a,b = read_n_bytes(str, pos, 2)
return new_pos, lshift(a, 8) + b
end
local read_int32 = function(str, pos)
local new_pos,a,b,c,d = read_n_bytes(str, pos, 4)
return new_pos,
lshift(a, 24) +
lshift(b, 16) +
lshift(c, 8 ) +
d
end
local pack_bytes = string.char
local write_int8 = pack_bytes
local write_int16 = function(v)
return pack_bytes(rshift(v, 8), band(v, 0xFF))
end
local write_int32 = function(v)
return pack_bytes(
band(rshift(v, 24), 0xFF),
band(rshift(v, 16), 0xFF),
band(rshift(v, 8), 0xFF),
band(v, 0xFF)
)
end
-- used for generate key random ops
math.randomseed(os.time())
-- SHA1 hashing from luacrypto, if available
local sha1_crypto
local done,crypto = pcall(require,'crypto')
if done then
sha1_crypto = function(msg)
return crypto.digest('sha1',msg,true)
end
end
-- from wiki article, not particularly clever impl
local sha1_wiki = function(msg)
local h0 = 0x67452301
local h1 = 0xEFCDAB89
local h2 = 0x98BADCFE
local h3 = 0x10325476
local h4 = 0xC3D2E1F0
local bits = #msg * 8
-- append b10000000
msg = msg..schar(0x80)
-- 64 bit length will be appended
local bytes = #msg + 8
-- 512 bit append stuff
local fill_bytes = 64 - (bytes % 64)
if fill_bytes ~= 64 then
msg = msg..srep(schar(0),fill_bytes)
end
-- append 64 big endian length
local high = math.floor(bits/2^32)
local low = bits - high*2^32
msg = msg..write_int32(high)..write_int32(low)
assert(#msg % 64 == 0,#msg % 64)
for j=1,#msg,64 do
local chunk = msg:sub(j,j+63)
assert(#chunk==64,#chunk)
local words = {}
local next = 1
local word
repeat
next,word = read_int32(chunk, next)
tinsert(words, word)
until next > 64
assert(#words==16)
for i=17,80 do
words[i] = bxor(words[i-3],words[i-8],words[i-14],words[i-16])
words[i] = rol(words[i],1)
end
local a = h0
local b = h1
local c = h2
local d = h3
local e = h4
for i=1,80 do
local k,f
if i > 0 and i < 21 then
f = bor(band(b,c),band(bnot(b),d))
k = 0x5A827999
elseif i > 20 and i < 41 then
f = bxor(b,c,d)
k = 0x6ED9EBA1
elseif i > 40 and i < 61 then
f = bor(band(b,c),band(b,d),band(c,d))
k = 0x8F1BBCDC
elseif i > 60 and i < 81 then
f = bxor(b,c,d)
k = 0xCA62C1D6
end
local temp = rol(a,5) + f + e + k + words[i]
e = d
d = c
c = rol(b,30)
b = a
a = temp
end
h0 = h0 + a
h1 = h1 + b
h2 = h2 + c
h3 = h3 + d
h4 = h4 + e
end
-- necessary on sizeof(int) == 32 machines
h0 = band(h0,0xffffffff)
h1 = band(h1,0xffffffff)
h2 = band(h2,0xffffffff)
h3 = band(h3,0xffffffff)
h4 = band(h4,0xffffffff)
return write_int32(h0)..write_int32(h1)..write_int32(h2)..write_int32(h3)..write_int32(h4)
end
local base64_encode = function(data)
return (mime.b64(data))
end
local DEFAULT_PORTS = {ws = 80, wss = 443}
local parse_url = function(url)
local protocol, address, uri = url:match('^(%w+)://([^/]+)(.*)$')
if not protocol then error('Invalid URL:'..url) end
protocol = protocol:lower()
local host, port = address:match("^(.+):(%d+)$")
if not host then
host = address
port = DEFAULT_PORTS[protocol]
end
if not uri or uri == '' then uri = '/' end
return protocol, host, tonumber(port), uri
end
local generate_key = function()
local r1 = mrandom(0,0xfffffff)
local r2 = mrandom(0,0xfffffff)
local r3 = mrandom(0,0xfffffff)
local r4 = mrandom(0,0xfffffff)
local key = write_int32(r1)..write_int32(r2)..write_int32(r3)..write_int32(r4)
assert(#key==16,#key)
return base64_encode(key)
end
return {
sha1 = sha1_crypto or sha1_wiki,
base64 = {
encode = base64_encode
},
parse_url = parse_url,
generate_key = generate_key,
read_int8 = read_int8,
read_int16 = read_int16,
read_int32 = read_int32,
write_int8 = write_int8,
write_int16 = write_int16,
write_int32 = write_int32,
}