This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Creation of custom package
#16
Try this:
Code:
local bit = require('bit') local ssl = require('ssl') local socket = require('socket') local encdec = require('encdec') local parse_url = require('socket.url').parse local bxor = bit.bxor local bor = bit.bor local band = bit.band local lshift = bit.lshift local rshift = bit.rshift local ssub = string.sub local sbyte = string.byte local schar = string.char local tinsert = table.insert local tconcat = table.concat local mmin = math.min local mfloor = math.floor local mrandom = math.random local base64enc = encdec.base64enc local sha1 = encdec.sha1 local unpack = unpack local CONTINUATION = 0 local TEXT = 1 local BINARY = 2 local CLOSE = 8 local PING = 9 local PONG = 10 local guid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' 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 write_int8 = schar local write_int16 = function(v)   return schar(rshift(v, 8), band(v, 0xFF)) end local write_int32 = function(v)   return schar(     band(rshift(v, 24), 0xFF),     band(rshift(v, 16), 0xFF),     band(rshift(v,  8), 0xFF),     band(v, 0xFF)   ) end local generate_key = function()   math.randomseed(os.time())   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)   return base64enc(key) end local bits = function(...)   local n = 0   for _,bitn in pairs{...} do     n = n + 2^bitn   end   return n end local bit_7 = bits(7) local bit_0_3 = bits(0,1,2,3) local bit_0_6 = bits(0,1,2,3,4,5,6) -- TODO: improve performance local xor_mask = function(encoded,mask,payload)   local transformed,transformed_arr = {},{}   -- xor chunk-wise to prevent stack overflow.   -- sbyte and schar multiple in/out values   -- which require stack   for p=1,payload,2000 do     local last = mmin(p+1999,payload)     local original = {sbyte(encoded,p,last)}     for i=1,#original do       local j = (i-1) % 4 + 1       transformed[i] = bxor(original[i],mask[j])     end     local xored = schar(unpack(transformed,1,#original))     tinsert(transformed_arr,xored)   end   return tconcat(transformed_arr) end local encode_header_small = function(header, payload)   return schar(header, payload) end local encode_header_medium = function(header, payload, len)   return schar(header, payload, band(rshift(len, 8), 0xFF), band(len, 0xFF)) end local encode_header_big = function(header, payload, high, low)   return schar(header, payload)..write_int32(high)..write_int32(low) end local encode = function(data,opcode,masked,fin)   local header = opcode or 1-- TEXT is default opcode   if fin == nil or fin == true then     header = bor(header,bit_7)   end   local payload = 0   if masked then     payload = bor(payload,bit_7)   end   local len = #data   local chunks = {}   if len < 126 then     payload = bor(payload,len)     tinsert(chunks,encode_header_small(header,payload))   elseif len <= 0xffff then     payload = bor(payload,126)     tinsert(chunks,encode_header_medium(header,payload,len))   elseif len < 2^53 then     local high = mfloor(len/2^32)     local low = len - high*2^32     payload = bor(payload,127)     tinsert(chunks,encode_header_big(header,payload,high,low))   end   if not masked then     tinsert(chunks,data)   else     local m1 = mrandom(0,0xff)     local m2 = mrandom(0,0xff)     local m3 = mrandom(0,0xff)     local m4 = mrandom(0,0xff)     local mask = {m1,m2,m3,m4}     tinsert(chunks,write_int8(m1,m2,m3,m4))     tinsert(chunks,xor_mask(data,mask,#data))   end   return tconcat(chunks) end local decode = function(encoded)   local encoded_bak = encoded   if #encoded < 2 then     return nil,2-#encoded   end   local pos,header,payload   pos,header = read_int8(encoded,1)   pos,payload = read_int8(encoded,pos)   local high,low   encoded = ssub(encoded,pos)   local bytes = 2   local fin = band(header,bit_7) > 0   local opcode = band(header,bit_0_3)   local mask = band(payload,bit_7) > 0   payload = band(payload,bit_0_6)   if payload > 125 then     if payload == 126 then       if #encoded < 2 then         return nil,2-#encoded       end       pos,payload = read_int16(encoded,1)     elseif payload == 127 then       if #encoded < 8 then         return nil,8-#encoded       end       pos,high = read_int32(encoded,1)       pos,low = read_int32(encoded,pos)       payload = high*2^32 + low       if payload < 0xffff or payload > 2^53 then         assert(false,'INVALID PAYLOAD '..payload)       end     else       assert(false,'INVALID PAYLOAD '..payload)     end     encoded = ssub(encoded,pos)     bytes = bytes + pos - 1   end   local decoded   if mask then     local bytes_short = payload + 4 - #encoded     if bytes_short > 0 then       return nil,bytes_short     end     local m1,m2,m3,m4     pos,m1 = read_int8(encoded,1)     pos,m2 = read_int8(encoded,pos)     pos,m3 = read_int8(encoded,pos)     pos,m4 = read_int8(encoded,pos)     encoded = ssub(encoded,pos)     local mask = {       m1,m2,m3,m4     }     decoded = xor_mask(encoded,mask,payload)     bytes = bytes + 4 + payload   else     local bytes_short = payload - #encoded     if bytes_short > 0 then       return nil,bytes_short     end     if #encoded > payload then       decoded = ssub(encoded,1,payload)     else       decoded = encoded     end     bytes = bytes + payload   end   return decoded,fin,opcode,encoded_bak:sub(bytes+1),mask end local encode_close = function(code,reason)   if code then     local data = write_int16(code)     if reason then       data = data..tostring(reason)     end     return data   end   return '' end local decode_close = function(data)   local _,code,reason   if data then     if #data > 1 then       _,code = read_int16(data,1)     end     if #data > 2 then       reason = data:sub(3)     end   end   return code,reason end local sec_websocket_accept = function(sec_websocket_key)   local enc = sha1(sec_websocket_key..guid, true)   return base64enc(enc) end local http_headers = function(request)   local headers = {}   if not request:match('.*HTTP/1%.1') then     return headers   end   request = request:match('[^\r\n]+\r\n(.*)')   for line in request:gmatch('[^\r\n]*\r\n') do     local name,val = line:match('([^%s]+)%s*:%s*([^\r\n]+)')     if name and val then       name = name:lower()       if not name:match('sec%-websocket') then         val = val:lower()       end       if not headers[name] then         headers[name] = val       else         headers[name] = headers[name]..','..val       end     elseif line ~= '\r\n' then       assert(false,line..'('..#line..')')     end   end   return headers,request:match('\r\n\r\n(.*)') end local upgrade_request = function(req, key, protocol)   local format = string.format   local lines = {     format('GET %s HTTP/1.1',req.path or ''),     format('Host: %s',req.host),     'Upgrade: websocket',     'Connection: Upgrade',     format('Sec-WebSocket-Key: %s',key),     'Sec-WebSocket-Version: 13',   }   if protocol then     tinsert(lines, format('Sec-WebSocket-Protocol: %s', protocol))   end   if req.port and req.port ~= 80 then     lines[2] = format('Host: %s:%d',req.host,req.port)   end   if req.userinfo then     local auth = format('Authorization: Basic %s', base64enc(req.userinfo))     tinsert(lines, auth)   end   tinsert(lines,'\r\n')   return tconcat(lines,'\r\n') end local receive = function(self)   if self.state ~= 'OPEN' and not self.is_closing then     return nil,nil,false,1006,'wrong state'   end   local first_opcode   local frames   local bytes = 3   local encoded = ''   local clean = function(was_clean,code,reason)     self.state = 'CLOSED'     self:sock_close()     if self.on_close then       self:on_close()     end     return nil,nil,was_clean,code,reason or 'closed'   end   while true do     local chunk,err = self:sock_receive(bytes)     if err then       if err == 'timeout' then         return nil,nil,false,1006,err       else         return clean(false,1006,err)       end     end     encoded = encoded..chunk     local decoded,fin,opcode,_,masked = decode(encoded)     if masked then       return clean(false,1006,'Websocket receive failed: frame was not masked')     end     if decoded then       if opcode == CLOSE then         if not self.is_closing then           local code,reason = decode_close(decoded)           -- echo code           local msg = encode_close(code)           local encoded = encode(msg,CLOSE,true)           local n,err = self:sock_send(encoded)           if n == #encoded then             return clean(true,code,reason)           else             return clean(false,code,err)           end         else           return decoded,opcode         end       end       if not first_opcode then         first_opcode = opcode       end       if not fin then         if not frames then           frames = {}         elseif opcode ~= CONTINUATION then           return clean(false,1002,'protocol error')         end         bytes = 3         encoded = ''         tinsert(frames,decoded)       elseif not frames then         return decoded,first_opcode       else         tinsert(frames,decoded)         return tconcat(frames),first_opcode       end     else       assert(type(fin) == 'number' and fin > 0)       bytes = fin     end   end end local send = function(self,data,opcode)   if self.state ~= 'OPEN' then     return nil,false,1006,'wrong state'   end   local encoded = encode(data,opcode or TEXT,true)   local n,err = self:sock_send(encoded)   if n ~= #encoded then     return nil,self:close(1006,err)   end   return true end local close = function(self,code,reason)   if self.state ~= 'OPEN' then     return false,1006,'wrong state'   end   if self.state == 'CLOSED' then     return false,1006,'wrong state'   end   local msg = encode_close(code or 1000,reason)   local encoded = encode(msg,CLOSE,true)   local n,err = self:sock_send(encoded)   local was_clean = false   code = 1005   reason = ''   if n == #encoded then     self.is_closing = true     local rmsg,opcode = self:receive()     if rmsg and opcode == CLOSE then       code,reason = decode_close(rmsg)       was_clean = true     end   else     reason = err   end   self:sock_close()   if self.on_close then     self:on_close()   end   self.state = 'CLOSED'   return was_clean,code,reason or '' end local DEFAULT_PORTS = {ws = 80, wss = 443} local connect = function(self,ws_url,ssl_params)   if self.state ~= 'CLOSED' then     return nil,'wrong state',nil   end   local parsed = parse_url(ws_url)   if parsed.scheme ~= 'wss' and parsed.scheme ~= 'ws' then     return nil, 'bad protocol'   end   if not parsed.port then     parsed.port = DEFAULT_PORTS[ parsed.scheme ]   end   -- Preconnect (for SSL if needed)   local _,err = self:sock_connect(parsed.host, parsed.port)   if err then     return nil,err,nil   end   if parsed.scheme == 'wss' then     if type(ssl_params) ~= 'table' then       ssl_params = {         protocol = 'tlsv1',         options  = {'all', 'no_sslv2', 'no_sslv3'},         verify   = 'none',       }     end     ssl_params.mode = 'client'     self.sock = ssl.wrap(self.sock, ssl_params)     self.sock:dohandshake()   elseif parsed.scheme ~= 'ws' then     return nil, 'bad protocol'   end   local key = generate_key()   local req = upgrade_request(parsed, key, self.protocol)   local n,err = self:sock_send(req)   if n ~= #req then     return nil,err,nil   end   local resp = {}   repeat     local line,err = self:sock_receive('*l')     resp[#resp+1] = line     if err then       return nil,err,nil     end   until line == ''   local response = tconcat(resp,'\r\n')   local headers = http_headers(response)   local expected_accept = sec_websocket_accept(key)   if headers['sec-websocket-accept'] ~= expected_accept then     local msg = 'Websocket Handshake failed: Invalid Sec-Websocket-Accept (expected %s got %s)'     return nil,msg:format(expected_accept,headers['sec-websocket-accept'] or 'nil'),headers   end   self.state = 'OPEN'   return true,headers['sec-websocket-protocol'],headers end local extend = function(obj)   obj.state = 'CLOSED'   obj.receive = receive   obj.send = send   obj.close = close   obj.connect = connect   return obj end local client_copas = function(timeout)   local copas = require('copas')   local self = {}   self.sock_connect = function(self,host,port)     self.sock = socket.tcp()     self.sock:settimeout(timeout or 5)     local _,err = copas.connect(self.sock,host,port)     if err and err ~= 'already connected' then       self.sock:close()       return nil,err     end   end   self.sock_send = function(self,...)     return copas.send(self.sock,...)   end   self.sock_receive = function(self,...)     return copas.receive(self.sock,...)   end   self.sock_close = function(self)     self.sock:close()   end   self = extend(self)   return self end local client_sync = function(timeout)   local self = {}   self.sock_connect = function(self,host,port)     self.sock = socket.tcp()     self.sock:settimeout(timeout or 5)     local _,err = self.sock:connect(host,port)     if err then       self.sock:close()       return nil,err     end   end   self.sock_send = function(self,...)     return self.sock:send(...)   end   self.sock_receive = function(self,...)     return self.sock:receive(...)   end   self.sock_close = function(self)     self.sock:close()   end   self = extend(self)   return self end local client = function(mode, timeout)   if mode == 'copas' then     return client_copas(timeout)   else     return client_sync(timeout)   end end return {   client = client,   CONTINUATION = CONTINUATION,   TEXT = TEXT,   BINARY = BINARY,   CLOSE = CLOSE,   PING = PING,   PONG = PONG }
Reply


Messages In This Thread
Creation of custom package - by Bitver - 17.03.2018, 10:03
RE: Creation of custom package - by admin - 19.03.2018, 07:33
RE: Creation of custom package - by admin - 20.03.2018, 14:27
RE: Creation of custom package - by Alexander - 19.05.2021, 19:12
RE: Creation of custom package - by rocfusion - 20.03.2018, 18:53
RE: Creation of custom package - by rocfusion - 23.03.2018, 02:30
RE: Creation of custom package - by rocfusion - 23.03.2018, 06:04
RE: Creation of custom package - by admin - 23.03.2018, 11:30
RE: Creation of custom package - by rocfusion - 23.03.2018, 14:25
RE: Creation of custom package - by admin - 23.03.2018, 14:41
RE: Creation of custom package - by rocfusion - 26.03.2018, 08:42
RE: Creation of custom package - by nickd - 03.03.2019, 09:30
RE: Creation of custom package - by admin - 26.03.2018, 08:48
RE: Creation of custom package - by doshibn - 09.05.2020, 12:10
RE: Creation of custom package - by admin - 11.05.2020, 06:52
RE: Creation of custom package - by admin - 11.05.2020, 11:24
RE: Creation of custom package - by admin - 20.05.2021, 05:27
RE: Creation of custom package - by myg - 05.05.2023, 21:26
RE: Creation of custom package - by admin - 08.05.2023, 09:02
RE: Creation of custom package - by admin - 11.12.2023, 12:47

Forum Jump: