Skip to content

Instantly share code, notes, and snippets.

@katlogic
Created April 12, 2014 00:13
Show Gist options
  • Save katlogic/10511191 to your computer and use it in GitHub Desktop.
Save katlogic/10511191 to your computer and use it in GitHub Desktop.
------------------------------------
-- basic TLS/SSL3.1 implementation
-- $Id: ssl.lua,v 1.13 2006-12-02 21:25:58 kt Exp $
------------------------------------
require "clua"
require "util"
write_allow = true
local function DEBUG()
end
--local function prettyhex()
--end
local DEBUG = print
----------------------------------
-- base64 decoder
----------------------------------
local b64t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
local btab = {}
local mask = "([%a%d%+/\r\n=]?)"
local packs = { "6n", "6n6n", "6n6n6n", "6n6n6n6n"}
for idx=1, #b64t, 1 do btab[b64t:sub(idx,idx)] = idx - 1 end
local function decode_base64(str)
str = string.gsub(str, "[\r\n]", "")
local bufout = buf.alloc(#str)
for c1,c2,c3,c4 in string.gmatch(str , mask..mask..mask..mask) do
local v = { btab[c1], btab[c2], btab[c3], btab[c4] }
if not v[1] then break end
bufout:pack(packs[#v], unpack(v))
end
return bufout
end
local function decode_pem(str, mark)
return string.sub(decode_base64(string.match(str, "%-%-%-%-%-BEGIN " .. mark .. "%-%-%-%-%-(.-)%-%-%-%-%-END " .. mark.."%-%-%-%-%-")), 1)
end
local function wouldblock()
local e = err.no
return e == "EWOULDBLOCK" or e == "EAGAIN" or e == "EINTR"
end
----------------------------------
-- asn1 decoder
----------------------------------
local asn1tab = {
[2] = "INT",
[3] = "BITSTR",
[4] = "BYTESTR",
[5] = "NULL",
[6] = "OID",
[0x13] = "PRINTABLE",
[0x30] = "SEQ",
[0x31] = "SET",
[0x80] = "ITAG",
[0xa0] = "ETAG"
}
-- return: obj, type, rest
local function asn1(str)
local typ, long, ln = buf.unpack(str, "8n1n7n")
local skip = 2
-- print("ASN1 typ=",typ," ln=", ln)
typ = asn1tab[typ]
-- long form
if (long == 1) then
assert(ln <= 4)
skip = $ + ln
ln = buf.unpack(str, 2, ln*8 .. "n")
end
--print("skip=\$skip\ ln=\$ln\")
return string.sub(str, skip + 1, skip + ln), typ, string.sub(str, skip + 1 + ln)
end
------------------------------------------
-- certificate decoder, returns public key
------------------------------------------
function decode_cert(src)
local modulus, exponent,_
local obj,typ,rest = asn1(asn1(asn1(src)))
if typ == "ETAG" then
_,typ,rest = asn1(rest)
--print(typ)
end
assert(typ == "INT")
obj,typ,rest = asn1(rest)
assert(typ == "SEQ")
obj,typ = asn1(obj)
-- SHA1/MD5 RSA signatures only
assert(obj:sub(1,8) == "\x2a\x86\x48\x86\xf7\x0d\x01\x01")
_,typ,rest = asn1(rest) -- issuer
_,typ,rest = asn1(rest) -- valid date
_,typ,rest = asn1(rest) -- subject
obj,typ,rest = asn1(rest) -- public key info in obj
_,typ,rest = asn1(obj) -- algorithm
obj,typ,rest = asn1(rest) -- public key
obj,typ = asn1(obj:sub(2)) -- rsa public key
modulus,typ,rest = asn1(obj)
assert(typ == "INT")
exponent,typ = asn1(rest)
assert(typ == "INT")
local rsa = crypto.rsa(modulus, exponent)
return rsa
end
------------------------------------------
-- private key decoder, returns privkey
------------------------------------------
function decode_key(src)
local modulus,p,q,dQ,dP,qInv
local obj,typ,rest = asn1(src)
local _
assert(typ == "SEQ")
obj,typ,rest = asn1(obj) -- version?
modulus,typ,rest = asn1(rest)
_,typ,rest = asn1(rest) -- pub exp
_,typ,rest = asn1(rest) -- priv exp
p,typ,rest = asn1(rest)
q,typ,rest = asn1(rest)
dP,typ,rest = asn1(rest)
dQ,typ,rest = asn1(rest)
qInv,typ,rest = asn1(rest)
local rsa = crypto.rsa(modulus, nil, p, q, dP, dQ, qInv)
return rsa
end
function ssl_load_cert(path)
return decode_cert(decode_pem(io.open(pwd):read("*a"), "CERTIFICATE"))
end
function ssl_load_key(path)
return decode_key(decode_pem(io.open(pwd):read("*a"), "RSA PRIVATE KEY"))
end
function ssl_init_certkey(cert,key)
key = key or cert
local ctx = {}
ctx.cert = assert(decode_pem(io.open(cert):read("*a"), "CERTIFICATE"))
ctx.pubkey = assert(decode_cert(ctx.cert))
ctx.privkey = assert(decode_key(decode_pem(io.open(key):read("*a"), "RSA PRIVATE KEY")))
return ctx
end
------------------------------------------
-- base ssl
------------------------------------------
-- 20 - change cipher spec
-- 21 - alert proto
-- 22 - handshake protocol
-- 23 - app data
--
-- 0 - hs_hello_request
-- 1 - client hello
-- 2 - server hello
-- 11 - certificate
-- 12 - server key xch
-- 13 - cert req
-- 14 - serv hello done
-- 15 - cert verify
-- 16 - client key xch
-- 20 - finished
-- prepare buffers
local function init_buf(ctx)
ctx.sendbuf = ctx.sendbuf or buf.alloc(16384)
ctx.recvbuf = ctx.recvbuf or buf.alloc(16384)
ctx.recvpos = ctx.recvpos or 0
-- shift data, if any
-- if (ctx.recvbuf and ctx.recvpos and ctx.recvpos > 0) then
-- ctx.recvbuf:off(0)
-- ctx.recvbuf:append(ctx.recvbuf, ctx.recvpos)
-- ctx.recvpos = 0
-- end
end
local function rc4_sha1_alg()
end
local cipher_suites = {
0x05, -- RC4-SHA1
-- 0x04, -- RC4-MD5
--[[
0x2f, -- AES128-SHA1
0x35 -- AES256-SHA1 ]]
}
local cipher_algs = {
[0x05] = rc4_sha1_alg,
}
---------------------------------------
-- packet sending
---------------------------------------
local function compute_hmac(ctx,src)
assert(ctx)
local writeseq = buf.pack(nil, "8B", unpack(ctx.seq))
-- prettyhex("writeseq", writeseq)
local ourmac
if ctx.minor == 1 then
ourmac = ctx.maco:
update(ctx.writemaco):
final(ctx.maci:
update(ctx.writemaci):
update(writeseq):
final(src))
prettyhex("computing hmac of:", writeseq..tostring(src))
else
ourmac = ctx.maco:
update(ctx.writemaco):
final(ctx.maci:
update(ctx.writemaci):
update(writeseq):
update(string.sub(src,1,1)):
update(src, 3):final())
prettyhex("computing hmac of:", writeseq..string.sub(src,1,1)..string.sub(src,4))
end
prettyhex("result mac: ", ourmac)
--prettyhex("ourmac", ourmac)
for i=8, 1, -1 do
if (ctx.seq[i] < 255) then
ctx.seq[i] = $+1
break
end
ctx.seq[i] = 0
end
return ourmac
end
local function send_raw_packet(ctx,proto)
local sbuf = ctx.sendbuf
local wsz = sbuf:off(0)
DEBUG("wsz is ", wsz)
if (wsz <= 5) then sbuf:off(wsz) return end
sbuf:pack("BBBH", proto, 3, ctx.minor or 1, wsz - 5)
local pos = 0
sbuf:off(wsz)
-- encrypt the packet
if (ctx.writec) then
DEBUG("SBUF", sbuf:off())
-- hmac is computed on plaintext
sbuf:append(compute_hmac(ctx, sbuf))
-- encryption in-place
ctx.writec:crypt(sbuf, sbuf, 5)
-- patch in the new size (this is so braindamaged!!!)
wsz = sbuf:off(3)
sbuf:pack("H", wsz - 5)
sbuf:off(wsz)
end
--prettyhex("send_raw", sbuf)
local _, got = ctx.s:send(sbuf, pos)
assert(not got)
--[[
while pos < wsz do
local sent = s.s:send(sbuf, pos)
if (not sent and wouldblock()) then
s.s:writewait()
continue
else
assert(sent and sent > 0)
end
pos = $ + sent
end]]
sbuf:off(5)
-- buf:off was set to 5 by buf:pack
end
--log = io.open("hslog", "w+")
local function hs_log(b, off, len)
local qq = string.sub(b, off + 1, len and len + off)
log:write(qq)
log:flush()
end
local function update_hash(ctx,b, ptr, len)
assert(ctx)
-- hs_log(b, ptr, len)
ctx.hs1:update(b, ptr, len)
ctx.hs2:update(b, ptr, len)
end
local function send_hs_packet(ctx)
update_hash(ctx, ctx.sendbuf,5)
return send_raw_packet(ctx, 22)
end
---------------------------------------
-- packet receiving
---------------------------------------
local function init_hash(ctx)
ctx.hs1 = crypto.md5()
ctx.hs2 = crypto.sha1()
end
local function check_ver(ctx,major,minor)
if not ctx.minor then
assert(minor == 0 or minor == 1)
ctx.minor = minor
end
assert(major == 3 and ctx.minor == minor)
end
local recv_raw_packet
local function raw_recv_packet(ctx,proto)
local rbuf = ctx.recvbuf
DEBUG("inside raw_recv_packet")
assert(proto)
-- we expect "upper protocol"
rbuf:off(0)
local _, left= ctx.s:recv(rbuf, 5)
DEBUG(_,left,rbuf:off())
assert(not left)
-- unpack the header
--print(#hdr)
local hproto, hmajor, hminor, hlen = buf.unpack(rbuf, "BBBH")
-- make sure the protocol is the requested one
--print(hproto, hmajor, hminor)
prettyhex("SSL proto header", rbuf)
-- v23 client hello hack
if (hproto >= 128 and proto == 22) then
local tmp, cmd,major,minor
tmp,hlen,cmd,major,minor = buf.unpack(rbuf, "1n15nBBB")
check_ver(ctx,major,minor)
assert(cmd == 1)
hlen=$-3
--print(hlen)
update_hash(ctx, rbuf, 2, 3)
else
DEBUG(hproto, hmajor, hminor)
check_ver(ctx, hmajor, hminor)
end
-- must be at least 4 bytes
-- print("HLEN",hlen)
--assert(hlen >= 4)
rbuf:off(0)
-- read the whole packet
ctx.proto = hproto
ctx.recpos = 0
local _, left = ctx.s:recv(rbuf, hlen)
assert(not left)
-- and decrypt it
if ctx.readc then
-- print(ctx.recpos, rbuf:off())
prettyhex("encrypted packet---", rbuf)
ctx.readc:crypt(rbuf, rbuf)
------------ print(ctx.recpos, rbuf:off())
prettyhex("decrypted packet---", rbuf)
assert(rbuf:off() >= 20)
if (hproto == 22 and proto == 23) then
return recv_raw_packet(ctx,proto)
end
assert(hproto == proto and hlen <= 16384)
return rbuf:off(rbuf:off()-20)-20
end
return rbuf:off()
end
recv_raw_packet = raw_recv_packet
-- get one record _header_, use recv_record_data to get the actual data
local function recv_hs_header(ctx)
-- there must not be record data left
assert(ctx.tbr == 0)
-- print(ctx.recpos, s.recvbuf:off())
-- we've nothing left inside the packet buffer, read a new one
if (ctx.recpos >= ctx.recvbuf:off()) then
local l = raw_recv_packet(ctx, 22)
-- continuation of v23 hack
if (ctx.proto >= 128) then
ctx.recpos = l
-- print("HASHING ", l)
update_hash(ctx, ctx.recvbuf, 0, l)
return 128, l
end
end
local code, len = ctx.recvbuf:unpack(ctx.recpos, "B24n")
assert(code and len)
ctx.tbr = len
update_hash(ctx, ctx.recvbuf, ctx.recpos, 4)
ctx.recpos = $+4
return code, len
end
-- return as much record data as possible in this iteration, actually,
-- offset to recvbuf and length is returned or nil on end of record
local function recv_hs_data(ctx, reqlen)
if (ctx.tbr <= 0) then return nil end
-- we've reached end of packet, read new one
if (ctx.recpos >= ctx.recvbuf:off()) then
raw_recv_packet(ctx, ctx.proto)
end
local pend = ctx.recvbuf:off()
local pstart = ctx.recpos
local plen = pend - pstart
assert(plen > 0)
if (reqlen) then
--print(reqlen, plen)
assert(reqlen <= plen)
plen = reqlen
-- packet is longer than record
elseif (plen > ctx.tbr) then
local tb = ctx.tbr
ctx.recpos = $ + tb
ctx.tbr = 0
update_hash(ctx, ctx.recvbuf, pstart, tb)
return pstart, tb
end
-- smaller or same
ctx.tbr = $-plen
ctx.recpos = $+plen
update_hash(ctx, ctx.recvbuf, pstart, plen)
return pstart, plen
end
-- return code, start, len
local function recv_hs(ctx)
local code, l = recv_hs_header(ctx)
if (code == 128) then
return code, 0, l
end
local ptr, len = recv_hs_data(ctx)
return code, ptr, len
end
-------------------------------------
-- HMAC functions
-------------------------------------
-- md5/sha1 prf part
local function pmac(func, hmaci, hmaco, seed, need)
local res = {}
local pos = need
local aX = seed
local s1 = func()
local s2 = func()
while pos > 0 do
aX = s1:update(hmaco):final(s2:update(hmaci):final(aX))
local tm = s1:update(hmaco):final(s2:update(hmaci):update(aX):final(seed))
table.insert(res, tm)
pos = pos - #tm
end
return table.concat(res)
end
-- tls prf
local function prf(secret, seed, len)
local S1 = secret:sub(1, (#secret + 1) >> 1)
local S2 = secret:sub((#secret >> 1) + 1)
local hmaci1, hmaco1 = crypto.hmac(S1)
local hmaci2, hmaco2 = crypto.hmac(S2)
local pmd5 = pmac(crypto.md5, hmaci1, hmaco1, seed, len)
local psha1 = pmac(crypto.sha1, hmaci2, hmaco2, seed, len)
local res = {}
for i=1, len, 1 do
table.insert(res, psha1:byte(i) ^^ pmd5:byte(i))
end
return string.char(unpack(res))
end
local pad_1_sha = string.rep("\x36", 40)
local pad_1_md5 = string.rep("\x36", 48)
local pad_2_sha = string.rep("\x5c", 40)
local pad_2_md5 = string.rep("\x5c", 48)
local function prf3(secret,rand,len)
local out = ""
local iter = 1
while #out < len do
DEBUG("iter=",iter," #out=", #out, "salt=", string.rep(string.char(64+iter), iter))
out = out..crypto.md5(secret..crypto.sha1(string.rep(string.char(64+iter), iter)..secret..rand))
iter=iter+1
end
return string.sub(out, 1, len)
end
local function ssl3_finished_digest(ctx,secret,sender)
return crypto.md5(secret .. pad_2_md5 .. ctx.hs1:final(sender .. secret .. pad_1_md5)) ..
crypto.sha1(secret .. pad_2_sha .. ctx.hs2:final(sender .. secret .. pad_1_sha))
end
local function ssl_init(s,ctx)
ctx.tbr = ctx.tbr or 0
ctx.recpos = ctx.recpos or 0
ctx.seq = ctx.seq or { 0,0,0,0,0,0,0,0}
ctx.maxpad = 20;
ctx.s = s
init_hash(ctx)
init_buf(ctx)
end
local function compute_master(ctx,premaster,macoff,clirand,servrand)
local master, keyblock
if (ctx.minor==1) then
master = prf(premaster, "master secret" .. clirand .. servrand, 48)
keyblock = prf(master, "key expansion" .. servrand .. clirand, 72)
ctx.writemaci, ctx.writemaco = crypto.hmac(keyblock:sub(macoff+1,macoff+20)) -- 21 - 40 is server's mac, we dont care
else
master = prf3(premaster, clirand..servrand, 48)
keyblock = prf3(master, servrand..clirand, 72)
prettyhex("keyblock", keyblock)
ctx.writemaci, ctx.writemaco = keyblock:sub(macoff+1,macoff+20) .. pad_1_sha, keyblock:sub(macoff+1,macoff+20) .. pad_2_sha
end
return master, keyblock
end
local function compute_final_digest(ctx, master, seed31, seed30)
local sbuf = ctx.sendbuf
ctx.maci = crypto.sha1()
ctx.maco = crypto.sha1()
if ctx.minor==1 then
local finaldigest = prf(master, seed31 .. ctx.hs1:final() .. ctx.hs2:final(), 12)
sbuf:pack("B24ns", 20, 12, finaldigest)
else
local finaldigest = ssl3_finished_digest(ctx, master, seed30)
sbuf:pack("B24ns", 20, #finaldigest, finaldigest)
end
--print("final digest", phex(finaldigest))
end
function ssl_server(s, ctx, sctx)
ssl_init(s, ctx)
local sbuf = ctx.sendbuf
local rbuf = ctx.recvbuf
-----------------------
-- client hello
-----------------------
local code, ptr, len = recv_hs(ctx)
local ciphid, clirand
-- client v23 hello hack
if (code == 128) then
local istls
local cslen, sidlen, chlen = rbuf:unpack("HHH")
DEBUG("SIDLEN",sidlen,chlen)
assert(sidlen == 0)
for i=0,cslen-1,3 do
istls, ciphid = rbuf:unpack(3 + i, "BH")
if (istls == 0 and cipher_algs[ciphid]) then
break
end
ciphid = nil
end
clirand = rbuf:unpack(6 + cslen, chlen.."S")
clirand = string.rep("\000", 32 - chlen) .. clirand
prettyhex("clirand", clirand)
assert(#clirand == 32)
else
-- standard TLS hello
assert(code == 1)
local major, minor, nciph
major, minor, clirand, nciph = rbuf:unpack(ptr, "BB32SxH")
assert(major == 3, minor == 1)
ptr=$+37
for i=0,nciph-1,2 do
ciphid = rbuf:unpack(ptr + i, "H")
if (cipher_algs[ciphid]) then
break
end
ciphid = nil
end
end
assert(ciphid)
-- we dont care for compressions, 0 is always supported
--------------------
-- server hello
------------------
sbuf:off(5)
local servrand = crypto.random(32)
sbuf:pack("B24nBBsBsHx", 2, 70, 3, ctx.minor, servrand, 32, crypto.random(32), ciphid)
send_hs_packet(ctx)
---------------------
-- server certificate
---------------------
sbuf:pack("B24n24n24ns", 11, #sctx.cert + 6, #sctx.cert + 3, #sctx.cert, sctx.cert)
send_hs_packet(ctx)
---------------------
-- server hello done
---------------------
sbuf:pack("B24n", 14, 0)
send_hs_packet(ctx)
----------------------
-- client key exchange
----------------------
code, ptr, len = recv_hs(ctx)
-- print(code)
assert(code == 16)
local enclen = (ctx.minor==1 and rbuf:unpack(ptr, "H")) or len
local encpre = rbuf:unpack((ctx.minor==1) and ptr+2 or ptr, enclen.."S")
DEBUG("PREM", enclen,#encpre)
local premaster = sctx.privkey:decrypt(encpre)
prettyhex("decrypted premaster", premaster)
local master, keyblock = compute_master(ctx, premaster, 20, clirand, servrand)
local rc = crypto.rc4(keyblock:sub(41, 56))
local wc = crypto.rc4(keyblock:sub(57,72))
----------------------
-- client change cipher spec
----------------------
raw_recv_packet(ctx,20)
code = rbuf:unpack(ctx.recpos, "B")
assert(code == 1)
rbuf:off(0) --to make recv_hs happy
ctx.readc = rc
--------------------------
-- client finished message
--------------------------
code, ptr, len = recv_hs(ctx)
prettyhex("CLIENT FINISHED",rbuf)
DEBUG(code)
assert(code == 20)
---------------------------
-- server changecipher spec
---------------------------
sbuf:pack("B", 1)
send_raw_packet(ctx, 20)
ctx.writec = wc
------------------------------------------
-- server finished. we actually don't care
------------------------------------------
compute_final_digest(ctx, master, "server finished", "SRVR")
send_raw_packet(ctx, 22)
ctx.recpos = 0;
rbuf:off(0)
sbuf:off(5)
end
-------------------------------------
-- SSL client
-------------------------------------
-- start ssl client on the connection
function ssl_client(s,ctx)
ssl_init(s, ctx)
local sbuf = ctx.sendbuf
local rbuf = ctx.recvbuf
assert(sbuf)
assert(rbuf)
--------------
-- helo packet
--------------
local clirand = buf.pack(nil, "Is",
event.now/1000,
crypto.random(28))
prettyhex("clirand", clirand)
sbuf:off(5)
sbuf:pack("B24nBB32SxH",
1,
-- size of message: 1+1+32+1+2+#cipher_suites*2+2 => major,minor,random,nosessid,ncipher,<ciphers>,nocompr
39 + #cipher_suites * 2,
3, 1, clirand, #cipher_suites * 2)
-- ciphers we can agree on
for i=1,#cipher_suites,1 do
sbuf:pack("H", cipher_suites[i])
end
-- null compression
sbuf:pack("Bx", 1)
send_hs_packet(ctx)
---------------------
-- server helo packet
---------------------
local code, ptr, len = recv_hs(ctx)
DEBUG("PTR", ptr)
local major, minor, servrand, sidlen = rbuf:unpack(ptr, "BB32SB")
assert(code == 2 and major == 3 and minor == ctx.minor and servrand and sidlen)
-- major, minor, rand, sidlen
local cipher, comp = rbuf:unpack(ptr + 2+32+1 + sidlen, "HB")
assert(comp == 0 and cipher_algs[cipher])
---------------------
-- server certificate
---------------------
code, len = recv_hs_header(ctx)
assert(code == 11)
ptr = recv_hs_data(ctx, 6)
-- get first cert size
local allcertlen, certlen = rbuf:unpack(ptr, "24n24n")
-- print("ALL", allcertlen, certlen)
ptr = recv_hs_data(ctx, certlen)
-- decode first cert
local serverpub = decode_cert(string.sub(rbuf, ptr + 1, ptr + certlen))
allcertlen = $-certlen-3
while allcertlen > 0 do
ptr = recv_hs_data(ctx, 3)
certlen = rbuf:unpack(ptr, "24n")
recv_hs_data(ctx, certlen)
allcertlen = $-certlen-3
end
assert(allcertlen == 0)
---------------------
-- server hello done
---------------------
local wantcert
code, ptr, len = recv_hs(ctx)
if (code == 13) then
wantcert = true
code, ptr, len = recv_hs(ctx)
end
assert(code == 14 and not len)
---------------------
-- client certificate
---------------------
if wantcert then
-- we have nothign to offer yet
-- allcertlen = 0 => no certificates
sbuf:pack("B24n24n", 11, 3, 0)
send_hs_packet(ctx)
end
----------------------
-- client key exchange
----------------------
local premaster = buf.pack(nil, "BBs", 3, 1, crypto.random(46))
assert(#premaster == 48)
local encpre = serverpub:encrypt(premaster)
if ctx.minor == 1 then
sbuf:pack("B24nHs", 16, #encpre + 2, #encpre, encpre)
else
sbuf:pack("B24ns", 16, #encpre , encpre)
end
send_hs_packet(ctx)
local master, keyblock = compute_master(ctx, premaster, 0, clirand, servrand)
local wc = crypto.rc4(keyblock:sub(41, 56))
local rc = crypto.rc4(keyblock:sub(57,72))
----------------------
-- changecipher spec
----------------------
sbuf:pack("B", 1)
send_raw_packet(ctx, 20)
ctx.writec = wc
--------------------------
-- client finished message
--------------------------
compute_final_digest(ctx, master, "client finished", "CLNT")
send_raw_packet(ctx, 22)
----------------------
-- server's cipherspec
----------------------
-- code, len = recv_record(s, 20
raw_recv_packet(ctx,20)
code = rbuf:unpack(ctx.recpos, "B")
assert(code == 1)
-- print("all done")
rbuf:off(0) --to make recv_hs happy
ctx.readc = rc
------------------------------------------
-- server finished. we actually don't care
------------------------------------------
code, ptr, len = recv_hs(ctx)
assert(code == 20)
ctx.recpos = 0;
rbuf:off(0)
sbuf:off(5)
end
-------------------------------------
-------------------------------------
-- reading/writing
-------------------------------------
-------------------------------------
function ssl_flush(ctx)
send_raw_packet(ctx, 23)
end
function ssl_write(ctx,data)
local maxpad = ctx.maxpad
local total = data.off and data:off() or #data
local left = ctx.sendbuf:left()-maxpad
local pos = 0
local did
while pos < total do
DEBUG("LEFT=",left,ctx.sendbuf:off())
if left < 0 then
ssl_flush(ctx)
left = ctx.sendbuf:left()-maxpad
end
if (left < total-pos) then
did = ctx.sendbuf:append(data, pos, left)
else
did = ctx.sendbuf:append(data, pos, total-pos)
end
DEBUG("sbuf.off=", ctx.sendbuf:off())
pos = $+did
left = $-did
end
end
function ssl_read(ctx,len)
local rbuf = ctx.recvbuf
local off = rbuf:off()
local pos = 0
if (ctx.recpos == off) then
off = recv_raw_packet(ctx, 23)
--print("OFF", off)
assert(ctx.recpos < off)
end
--print("OFF", off, " recpos", ctx.recpos)
-- return whatever is left (remainder)
len = len or (off - ctx.recpos)
--print("nlen ", len)
-- simple case
if len + ctx.recpos <= off then
local r = string.sub(rbuf, ctx.recpos + 1, ctx.recpos + len)
ctx.recpos = $+len
return r
end
-- multipart read (slow)
local t = string.sub(rbuf, ctx.recpos + 1)
local ret = { t }
len = $ - #t
while len > 0 do
recv_raw_packet(ctx, 23)
t = string.sub(rbuf, len)
table.insert(ret, t)
len = len - #t
end
ctx.recpos = #t
return table.concat(ret)
end
function ssl_recvline(ctx)
local rbuf = ctx.recvbuf
local off = rbuf:off()
local saved = ""
while true do
DEBUG("still here?")
if ctx.recpos < rbuf:off() then
local ln, cr = string.match(rbuf, "(.-)(\r?)\n", ctx.recpos + 1)
if ln then
DEBUG("CTX", ctx.recpos, rbuf:off())
ctx.recpos = $ + #ln + #cr + 1
DEBUG("SSL_RECVLINE: '\$ln\'")
return saved .. ln
end
if ctx.recpos < rbuf:off() then
saved = saved .. string.sub(rbuf, ctx.recpos + 1)
end
end
DEBUG("waiting for packet")
off = recv_raw_packet(ctx, 23)
DEBUG("received packet")
assert(ctx.recpos < off)
end
end
write_allow = false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment