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.
8174 lines
157 KiB
8174 lines
157 KiB
package.preload['verse']=(function(...) |
|
package.preload['util.encodings']=(function(...) |
|
local function e() |
|
error("Function not implemented"); |
|
end |
|
local t=require"mime"; |
|
module"encodings" |
|
stringprep={}; |
|
base64={encode=t.b64,decode=e}; |
|
return _M; |
|
end) |
|
package.preload['util.hashes']=(function(...) |
|
local e=require"util.sha1"; |
|
return{sha1=e.sha1}; |
|
end) |
|
package.preload['util.sha1']=(function(...) |
|
local c=string.len |
|
local a=string.char |
|
local b=string.byte |
|
local g=string.sub |
|
local m=math.floor |
|
local t=require"bit" |
|
local k=t.bnot |
|
local e=t.band |
|
local y=t.bor |
|
local n=t.bxor |
|
local i=t.lshift |
|
local o=t.rshift |
|
local u,l,d,h,r |
|
local function p(e,t) |
|
return i(e,t)+o(e,32-t) |
|
end |
|
local function s(i) |
|
local t,o |
|
local t="" |
|
for n=1,8 do |
|
o=e(i,15) |
|
if(o<10)then |
|
t=a(o+48)..t |
|
else |
|
t=a(o+87)..t |
|
end |
|
i=m(i/16) |
|
end |
|
return t |
|
end |
|
local function j(t) |
|
local i,o |
|
local n="" |
|
i=c(t)*8 |
|
t=t..a(128) |
|
o=56-e(c(t),63) |
|
if(o<0)then |
|
o=o+64 |
|
end |
|
for e=1,o do |
|
t=t..a(0) |
|
end |
|
for t=1,8 do |
|
n=a(e(i,255))..n |
|
i=m(i/256) |
|
end |
|
return t..n |
|
end |
|
local function q(w) |
|
local m,t,i,o,f,s,c,v |
|
local a,a |
|
local a={} |
|
while(w~="")do |
|
for e=0,15 do |
|
a[e]=0 |
|
for t=1,4 do |
|
a[e]=a[e]*256+b(w,e*4+t) |
|
end |
|
end |
|
for e=16,79 do |
|
a[e]=p(n(n(a[e-3],a[e-8]),n(a[e-14],a[e-16])),1) |
|
end |
|
m=u |
|
t=l |
|
i=d |
|
o=h |
|
f=r |
|
for h=0,79 do |
|
if(h<20)then |
|
s=y(e(t,i),e(k(t),o)) |
|
c=1518500249 |
|
elseif(h<40)then |
|
s=n(n(t,i),o) |
|
c=1859775393 |
|
elseif(h<60)then |
|
s=y(y(e(t,i),e(t,o)),e(i,o)) |
|
c=2400959708 |
|
else |
|
s=n(n(t,i),o) |
|
c=3395469782 |
|
end |
|
v=p(m,5)+s+f+c+a[h] |
|
f=o |
|
o=i |
|
i=p(t,30) |
|
t=m |
|
m=v |
|
end |
|
u=e(u+m,4294967295) |
|
l=e(l+t,4294967295) |
|
d=e(d+i,4294967295) |
|
h=e(h+o,4294967295) |
|
r=e(r+f,4294967295) |
|
w=g(w,65) |
|
end |
|
end |
|
local function a(e,t) |
|
e=j(e) |
|
u=1732584193 |
|
l=4023233417 |
|
d=2562383102 |
|
h=271733878 |
|
r=3285377520 |
|
q(e) |
|
local e=s(u)..s(l)..s(d) |
|
..s(h)..s(r); |
|
if t then |
|
return e; |
|
else |
|
return(e:gsub("..",function(e) |
|
return string.char(tonumber(e,16)); |
|
end)); |
|
end |
|
end |
|
_G.sha1={sha1=a}; |
|
return _G.sha1; |
|
end) |
|
package.preload['lib.adhoc']=(function(...) |
|
local n,r=require"util.stanza",require"util.uuid"; |
|
local h="http://jabber.org/protocol/commands"; |
|
local i={} |
|
local s={}; |
|
function _cmdtag(e,o,t,a) |
|
local e=n.stanza("command",{xmlns=h,node=e.node,status=o}); |
|
if t then e.attr.sessionid=t;end |
|
if a then e.attr.action=a;end |
|
return e; |
|
end |
|
function s.new(e,a,t,o) |
|
return{name=e,node=a,handler=t,cmdtag=_cmdtag,permission=(o or"user")}; |
|
end |
|
function s.handle_cmd(o,s,a) |
|
local e=a.tags[1].attr.sessionid or r.generate(); |
|
local t={}; |
|
t.to=a.attr.to; |
|
t.from=a.attr.from; |
|
t.action=a.tags[1].attr.action or"execute"; |
|
t.form=a.tags[1]:child_with_ns("jabber:x:data"); |
|
local t,h=o:handler(t,i[e]); |
|
i[e]=h; |
|
local a=n.reply(a); |
|
if t.status=="completed"then |
|
i[e]=nil; |
|
cmdtag=o:cmdtag("completed",e); |
|
elseif t.status=="canceled"then |
|
i[e]=nil; |
|
cmdtag=o:cmdtag("canceled",e); |
|
elseif t.status=="error"then |
|
i[e]=nil; |
|
a=n.error_reply(a,t.error.type,t.error.condition,t.error.message); |
|
s.send(a); |
|
return true; |
|
else |
|
cmdtag=o:cmdtag("executing",e); |
|
end |
|
for t,e in pairs(t)do |
|
if t=="info"then |
|
cmdtag:tag("note",{type="info"}):text(e):up(); |
|
elseif t=="warn"then |
|
cmdtag:tag("note",{type="warn"}):text(e):up(); |
|
elseif t=="error"then |
|
cmdtag:tag("note",{type="error"}):text(e.message):up(); |
|
elseif t=="actions"then |
|
local t=n.stanza("actions"); |
|
for a,e in ipairs(e)do |
|
if(e=="prev")or(e=="next")or(e=="complete")then |
|
t:tag(e):up(); |
|
else |
|
module:log("error",'Command "'..o.name.. |
|
'" at node "'..o.node..'" provided an invalid action "'..e..'"'); |
|
end |
|
end |
|
cmdtag:add_child(t); |
|
elseif t=="form"then |
|
cmdtag:add_child((e.layout or e):form(e.values)); |
|
elseif t=="result"then |
|
cmdtag:add_child((e.layout or e):form(e.values,"result")); |
|
elseif t=="other"then |
|
cmdtag:add_child(e); |
|
end |
|
end |
|
a:add_child(cmdtag); |
|
s.send(a); |
|
return true; |
|
end |
|
return s; |
|
end) |
|
package.preload['util.rsm']=(function(...) |
|
local h=require"util.stanza".stanza; |
|
local a,i=tostring,tonumber; |
|
local n=type; |
|
local r=pairs; |
|
local s='http://jabber.org/protocol/rsm'; |
|
local o={}; |
|
do |
|
local e=o; |
|
local function t(e) |
|
return i((e:get_text())); |
|
end |
|
local function a(t) |
|
return t:get_text(); |
|
end |
|
e.after=a; |
|
e.before=function(e) |
|
local e=e:get_text(); |
|
return e==""or e; |
|
end; |
|
e.max=t; |
|
e.index=t; |
|
e.first=function(e) |
|
return{index=i(e.attr.index);e:get_text()}; |
|
end; |
|
e.last=a; |
|
e.count=t; |
|
end |
|
local d=setmetatable({ |
|
first=function(t,e) |
|
if n(e)=="table"then |
|
t:tag("first",{index=e.index}):text(e[1]):up(); |
|
else |
|
t:tag("first"):text(a(e)):up(); |
|
end |
|
end; |
|
before=function(t,e) |
|
if e==true then |
|
t:tag("before"):up(); |
|
else |
|
t:tag("before"):text(a(e)):up(); |
|
end |
|
end |
|
},{ |
|
__index=function(t,e) |
|
return function(t,o) |
|
t:tag(e):text(a(o)):up(); |
|
end |
|
end; |
|
}); |
|
local function n(e) |
|
local i={}; |
|
for t in e:childtags()do |
|
local e=t.name; |
|
local a=e and o[e]; |
|
if a then |
|
i[e]=a(t); |
|
end |
|
end |
|
return i; |
|
end |
|
local function i(e) |
|
local t=h("set",{xmlns=s}); |
|
for e,a in r(e)do |
|
if o[e]then |
|
d[e](t,a); |
|
end |
|
end |
|
return t; |
|
end |
|
local function t(e) |
|
local e=e:get_child("set",s); |
|
if e and#e.tags>0 then |
|
return n(e); |
|
end |
|
end |
|
return{parse=n,generate=i,get=t}; |
|
end) |
|
package.preload['util.stanza']=(function(...) |
|
local t=table.insert; |
|
local d=table.remove; |
|
local w=table.concat; |
|
local s=string.format; |
|
local l=string.match; |
|
local f=tostring; |
|
local u=setmetatable; |
|
local n=pairs; |
|
local i=ipairs; |
|
local o=type; |
|
local v=string.gsub; |
|
local y=string.sub; |
|
local c=string.find; |
|
local e=os; |
|
local m=not e.getenv("WINDIR"); |
|
local r,a; |
|
if m then |
|
local t,e=pcall(require,"util.termcolours"); |
|
if t then |
|
r,a=e.getstyle,e.getstring; |
|
else |
|
m=nil; |
|
end |
|
end |
|
local p="urn:ietf:params:xml:ns:xmpp-stanzas"; |
|
module"stanza" |
|
stanza_mt={__type="stanza"}; |
|
stanza_mt.__index=stanza_mt; |
|
local e=stanza_mt; |
|
function stanza(t,a) |
|
local t={name=t,attr=a or{},tags={}}; |
|
return u(t,e); |
|
end |
|
local h=stanza; |
|
function e:query(e) |
|
return self:tag("query",{xmlns=e}); |
|
end |
|
function e:body(t,e) |
|
return self:tag("body",e):text(t); |
|
end |
|
function e:tag(a,e) |
|
local a=h(a,e); |
|
local e=self.last_add; |
|
if not e then e={};self.last_add=e;end |
|
(e[#e]or self):add_direct_child(a); |
|
t(e,a); |
|
return self; |
|
end |
|
function e:text(t) |
|
local e=self.last_add; |
|
(e and e[#e]or self):add_direct_child(t); |
|
return self; |
|
end |
|
function e:up() |
|
local e=self.last_add; |
|
if e then d(e);end |
|
return self; |
|
end |
|
function e:reset() |
|
self.last_add=nil; |
|
return self; |
|
end |
|
function e:add_direct_child(e) |
|
if o(e)=="table"then |
|
t(self.tags,e); |
|
end |
|
t(self,e); |
|
end |
|
function e:add_child(t) |
|
local e=self.last_add; |
|
(e and e[#e]or self):add_direct_child(t); |
|
return self; |
|
end |
|
function e:get_child(a,t) |
|
for o,e in i(self.tags)do |
|
if(not a or e.name==a) |
|
and((not t and self.attr.xmlns==e.attr.xmlns) |
|
or e.attr.xmlns==t)then |
|
return e; |
|
end |
|
end |
|
end |
|
function e:get_child_text(e,t) |
|
local e=self:get_child(e,t); |
|
if e then |
|
return e:get_text(); |
|
end |
|
return nil; |
|
end |
|
function e:child_with_name(t) |
|
for a,e in i(self.tags)do |
|
if e.name==t then return e;end |
|
end |
|
end |
|
function e:child_with_ns(t) |
|
for a,e in i(self.tags)do |
|
if e.attr.xmlns==t then return e;end |
|
end |
|
end |
|
function e:children() |
|
local e=0; |
|
return function(t) |
|
e=e+1 |
|
return t[e]; |
|
end,self,e; |
|
end |
|
function e:childtags(i,t) |
|
local e=self.tags; |
|
local a,o=1,#e; |
|
return function() |
|
for o=a,o do |
|
local e=e[o]; |
|
if(not i or e.name==i) |
|
and((not t and self.attr.xmlns==e.attr.xmlns) |
|
or e.attr.xmlns==t)then |
|
a=o+1; |
|
return e; |
|
end |
|
end |
|
end; |
|
end |
|
function e:maptags(i) |
|
local a,t=self.tags,1; |
|
local n,o=#self,#a; |
|
local e=1; |
|
while t<=o and o>0 do |
|
if self[e]==a[t]then |
|
local i=i(self[e]); |
|
if i==nil then |
|
d(self,e); |
|
d(a,t); |
|
n=n-1; |
|
o=o-1; |
|
e=e-1; |
|
t=t-1; |
|
else |
|
self[e]=i; |
|
a[t]=i; |
|
end |
|
t=t+1; |
|
end |
|
e=e+1; |
|
end |
|
return self; |
|
end |
|
function e:find(a) |
|
local e=1; |
|
local s=#a+1; |
|
repeat |
|
local o,t,i; |
|
local n=y(a,e,e); |
|
if n=="@"then |
|
return self.attr[y(a,e+1)]; |
|
elseif n=="{"then |
|
o,e=l(a,"^([^}]+)}()",e+1); |
|
end |
|
t,i,e=l(a,"^([^@/#]*)([/#]?)()",e); |
|
t=t~=""and t or nil; |
|
if e==s then |
|
if i=="#"then |
|
return self:get_child_text(t,o); |
|
end |
|
return self:get_child(t,o); |
|
end |
|
self=self:get_child(t,o); |
|
until not self |
|
end |
|
local d |
|
do |
|
local t={["'"]="'",["\""]=""",["<"]="<",[">"]=">",["&"]="&"}; |
|
function d(e)return(v(e,"['&<>\"]",t));end |
|
_M.xml_escape=d; |
|
end |
|
local function y(a,e,h,o,r) |
|
local i=0; |
|
local s=a.name |
|
t(e,"<"..s); |
|
for a,n in n(a.attr)do |
|
if c(a,"\1",1,true)then |
|
local a,s=l(a,"^([^\1]*)\1?(.*)$"); |
|
i=i+1; |
|
t(e," xmlns:ns"..i.."='"..o(a).."' ".."ns"..i..":"..s.."='"..o(n).."'"); |
|
elseif not(a=="xmlns"and n==r)then |
|
t(e," "..a.."='"..o(n).."'"); |
|
end |
|
end |
|
local i=#a; |
|
if i==0 then |
|
t(e,"/>"); |
|
else |
|
t(e,">"); |
|
for i=1,i do |
|
local i=a[i]; |
|
if i.name then |
|
h(i,e,h,o,a.attr.xmlns); |
|
else |
|
t(e,o(i)); |
|
end |
|
end |
|
t(e,"</"..s..">"); |
|
end |
|
end |
|
function e.__tostring(t) |
|
local e={}; |
|
y(t,e,y,d,nil); |
|
return w(e); |
|
end |
|
function e.top_tag(e) |
|
local t=""; |
|
if e.attr then |
|
for e,a in n(e.attr)do if o(e)=="string"then t=t..s(" %s='%s'",e,d(f(a)));end end |
|
end |
|
return s("<%s%s>",e.name,t); |
|
end |
|
function e.get_text(e) |
|
if#e.tags==0 then |
|
return w(e); |
|
end |
|
end |
|
function e.get_error(a) |
|
local o,e,t; |
|
local a=a:get_child("error"); |
|
if not a then |
|
return nil,nil,nil; |
|
end |
|
o=a.attr.type; |
|
for o,a in i(a.tags)do |
|
if a.attr.xmlns==p then |
|
if not t and a.name=="text"then |
|
t=a:get_text(); |
|
elseif not e then |
|
e=a.name; |
|
end |
|
if e and t then |
|
break; |
|
end |
|
end |
|
end |
|
return o,e or"undefined-condition",t; |
|
end |
|
do |
|
local e=0; |
|
function new_id() |
|
e=e+1; |
|
return"lx"..e; |
|
end |
|
end |
|
function preserialize(e) |
|
local a={name=e.name,attr=e.attr}; |
|
for i,e in i(e)do |
|
if o(e)=="table"then |
|
t(a,preserialize(e)); |
|
else |
|
t(a,e); |
|
end |
|
end |
|
return a; |
|
end |
|
function deserialize(a) |
|
if a then |
|
local s=a.attr; |
|
for e=1,#s do s[e]=nil;end |
|
local h={}; |
|
for e in n(s)do |
|
if c(e,"|",1,true)and not c(e,"\1",1,true)then |
|
local t,a=l(e,"^([^|]+)|(.+)$"); |
|
h[t.."\1"..a]=s[e]; |
|
s[e]=nil; |
|
end |
|
end |
|
for t,e in n(h)do |
|
s[t]=e; |
|
end |
|
u(a,e); |
|
for t,e in i(a)do |
|
if o(e)=="table"then |
|
deserialize(e); |
|
end |
|
end |
|
if not a.tags then |
|
local n={}; |
|
for i,e in i(a)do |
|
if o(e)=="table"then |
|
t(n,e); |
|
end |
|
end |
|
a.tags=n; |
|
end |
|
end |
|
return a; |
|
end |
|
local function l(a) |
|
local o,i={},{}; |
|
for t,e in n(a.attr)do o[t]=e;end |
|
local o={name=a.name,attr=o,tags=i}; |
|
for e=1,#a do |
|
local e=a[e]; |
|
if e.name then |
|
e=l(e); |
|
t(i,e); |
|
end |
|
t(o,e); |
|
end |
|
return u(o,e); |
|
end |
|
clone=l; |
|
function message(e,t) |
|
if not t then |
|
return h("message",e); |
|
else |
|
return h("message",e):tag("body"):text(t):up(); |
|
end |
|
end |
|
function iq(e) |
|
if e and not e.id then e.id=new_id();end |
|
return h("iq",e or{id=new_id()}); |
|
end |
|
function reply(e) |
|
return h(e.name,e.attr and{to=e.attr.from,from=e.attr.to,id=e.attr.id,type=((e.name=="iq"and"result")or e.attr.type)}); |
|
end |
|
do |
|
local a={xmlns=p}; |
|
function error_reply(e,o,i,t) |
|
local e=reply(e); |
|
e.attr.type="error"; |
|
e:tag("error",{type=o}) |
|
:tag(i,a):up(); |
|
if(t)then e:tag("text",a):text(t):up();end |
|
return e; |
|
end |
|
end |
|
function presence(e) |
|
return h("presence",e); |
|
end |
|
if m then |
|
local u=r("yellow"); |
|
local h=r("red"); |
|
local l=r("red"); |
|
local t=r("magenta"); |
|
local r=" "..a(u,"%s")..a(t,"=")..a(h,"'%s'"); |
|
local h=a(t,"<")..a(l,"%s").."%s"..a(t,">"); |
|
local l=h.."%s"..a(t,"</")..a(l,"%s")..a(t,">"); |
|
function e.pretty_print(e) |
|
local t=""; |
|
for a,e in i(e)do |
|
if o(e)=="string"then |
|
t=t..d(e); |
|
else |
|
t=t..e:pretty_print(); |
|
end |
|
end |
|
local a=""; |
|
if e.attr then |
|
for e,t in n(e.attr)do if o(e)=="string"then a=a..s(r,e,f(t));end end |
|
end |
|
return s(l,e.name,a,t,e.name); |
|
end |
|
function e.pretty_top_tag(t) |
|
local e=""; |
|
if t.attr then |
|
for t,a in n(t.attr)do if o(t)=="string"then e=e..s(r,t,f(a));end end |
|
end |
|
return s(h,t.name,e); |
|
end |
|
else |
|
e.pretty_print=e.__tostring; |
|
e.pretty_top_tag=e.top_tag; |
|
end |
|
return _M; |
|
end) |
|
package.preload['util.timer']=(function(...) |
|
local a=require"net.server"; |
|
local d=math.min |
|
local l=math.huge |
|
local n=require"socket".gettime; |
|
local r=table.insert; |
|
local h=pairs; |
|
local s=type; |
|
local i={}; |
|
local e={}; |
|
module"timer" |
|
local t; |
|
if not a.event then |
|
function t(o,i) |
|
local n=n(); |
|
o=o+n; |
|
if o>=n then |
|
r(e,{o,i}); |
|
else |
|
local e=i(n); |
|
if e and s(e)=="number"then |
|
return t(e,i); |
|
end |
|
end |
|
end |
|
a._addtimer(function() |
|
local a=n(); |
|
if#e>0 then |
|
for a,t in h(e)do |
|
r(i,t); |
|
end |
|
e={}; |
|
end |
|
local e=l; |
|
for h,o in h(i)do |
|
local o,n=o[1],o[2]; |
|
if o<=a then |
|
i[h]=nil; |
|
local a=n(a); |
|
if s(a)=="number"then |
|
t(a,n); |
|
e=d(e,a); |
|
end |
|
else |
|
e=d(e,o-a); |
|
end |
|
end |
|
return e; |
|
end); |
|
else |
|
local e=a.event; |
|
local a=a.event_base; |
|
local o=(e.core and e.core.LEAVE)or-1; |
|
function t(i,e) |
|
local t; |
|
t=a:addevent(nil,0,function() |
|
local e=e(n()); |
|
if e then |
|
return 0,e; |
|
elseif t then |
|
return o; |
|
end |
|
end |
|
,i); |
|
end |
|
end |
|
add_task=t; |
|
return _M; |
|
end) |
|
package.preload['util.termcolours']=(function(...) |
|
local i,n=table.concat,table.insert; |
|
local t,a=string.char,string.format; |
|
local h=tonumber; |
|
local s=ipairs; |
|
local r=io.write; |
|
local e; |
|
if os.getenv("WINDIR")then |
|
e=require"util.windows"; |
|
end |
|
local o=e and e.get_consolecolor and e.get_consolecolor(); |
|
module"termcolours" |
|
local d={ |
|
reset=0;bright=1,dim=2,underscore=4,blink=5,reverse=7,hidden=8; |
|
black=30;red=31;green=32;yellow=33;blue=34;magenta=35;cyan=36;white=37; |
|
["black background"]=40;["red background"]=41;["green background"]=42;["yellow background"]=43;["blue background"]=44;["magenta background"]=45;["cyan background"]=46;["white background"]=47; |
|
bold=1,dark=2,underline=4,underlined=4,normal=0; |
|
} |
|
local u={ |
|
["0"]=o, |
|
["1"]=7+8, |
|
["1;33"]=2+4+8, |
|
["1;31"]=4+8 |
|
} |
|
local l={ |
|
[1]="font-weight: bold",[2]="opacity: 0.5",[4]="text-decoration: underline",[8]="visibility: hidden", |
|
[30]="color:black",[31]="color:red",[32]="color:green",[33]="color:#FFD700", |
|
[34]="color:blue",[35]="color: magenta",[36]="color:cyan",[37]="color: white", |
|
[40]="background-color:black",[41]="background-color:red",[42]="background-color:green", |
|
[43]="background-color:yellow",[44]="background-color:blue",[45]="background-color: magenta", |
|
[46]="background-color:cyan",[47]="background-color: white"; |
|
}; |
|
local c=t(27).."[%sm%s"..t(27).."[0m"; |
|
function getstring(t,e) |
|
if t then |
|
return a(c,t,e); |
|
else |
|
return e; |
|
end |
|
end |
|
function getstyle(...) |
|
local e,t={...},{}; |
|
for a,e in s(e)do |
|
e=d[e]; |
|
if e then |
|
n(t,e); |
|
end |
|
end |
|
return i(t,";"); |
|
end |
|
local a="0"; |
|
function setstyle(e) |
|
e=e or"0"; |
|
if e~=a then |
|
r("\27["..e.."m"); |
|
a=e; |
|
end |
|
end |
|
if e then |
|
function setstyle(t) |
|
t=t or"0"; |
|
if t~=a then |
|
e.set_consolecolor(u[t]or o); |
|
a=t; |
|
end |
|
end |
|
if not o then |
|
function setstyle(e)end |
|
end |
|
end |
|
local function a(e) |
|
if e=="0"then return"</span>";end |
|
local t={}; |
|
for e in e:gmatch("[^;]+")do |
|
n(t,l[h(e)]); |
|
end |
|
return"</span><span style='"..i(t,";").."'>"; |
|
end |
|
function tohtml(e) |
|
return e:gsub("\027%[(.-)m",a); |
|
end |
|
return _M; |
|
end) |
|
package.preload['util.uuid']=(function(...) |
|
local e=math.random; |
|
local o=tostring; |
|
local e=os.time; |
|
local n=os.clock; |
|
local i=require"util.hashes".sha1; |
|
module"uuid" |
|
local t=0; |
|
local function a() |
|
local e=e(); |
|
if t>=e then e=t+1;end |
|
t=e; |
|
return e; |
|
end |
|
local function t(e) |
|
return i(e..n()..o({}),true); |
|
end |
|
local e=t(a()); |
|
local function o(a) |
|
e=t(e..a); |
|
end |
|
local function t(t) |
|
if#e<t then o(a());end |
|
local a=e:sub(0,t); |
|
e=e:sub(t+1); |
|
return a; |
|
end |
|
local function e() |
|
return("%x"):format(t(1):byte()%4+8); |
|
end |
|
function generate() |
|
return t(8).."-"..t(4).."-4"..t(3).."-"..(e())..t(3).."-"..t(12); |
|
end |
|
seed=o; |
|
return _M; |
|
end) |
|
package.preload['net.dns']=(function(...) |
|
local s=require"socket"; |
|
local j=require"util.timer"; |
|
local e,v=pcall(require,"util.windows"); |
|
local _=(e and v)or os.getenv("WINDIR"); |
|
local u,z,b,a,i= |
|
coroutine,io,math,string,table; |
|
local c,n,o,f,r,p,k,x,t,e,q= |
|
ipairs,next,pairs,print,setmetatable,tostring,assert,error,unpack,select,type; |
|
local e={ |
|
get=function(t,...) |
|
local a=e('#',...); |
|
for a=1,a do |
|
t=t[e(a,...)]; |
|
if t==nil then break;end |
|
end |
|
return t; |
|
end; |
|
set=function(a,...) |
|
local s=e('#',...); |
|
local h,o=e(s-1,...); |
|
local t,i; |
|
for s=1,s-2 do |
|
local s=e(s,...) |
|
local e=a[s] |
|
if o==nil then |
|
if e==nil then |
|
return; |
|
elseif n(e,n(e))then |
|
t=nil;i=nil; |
|
elseif t==nil then |
|
t=a;i=s; |
|
end |
|
elseif e==nil then |
|
e={}; |
|
a[s]=e; |
|
end |
|
a=e |
|
end |
|
if o==nil and t then |
|
t[i]=nil; |
|
else |
|
a[h]=o; |
|
return o; |
|
end |
|
end; |
|
}; |
|
local d,l=e.get,e.set; |
|
local E=15; |
|
module('dns') |
|
local t=_M; |
|
local h=i.insert |
|
local function m(e) |
|
return(e-(e%256))/256; |
|
end |
|
local function w(e) |
|
local t={}; |
|
for o,e in o(e)do |
|
t[o]=e; |
|
t[e]=e; |
|
t[a.lower(e)]=e; |
|
end |
|
return t; |
|
end |
|
local function y(i) |
|
local e={}; |
|
for t,i in o(i)do |
|
local o=a.char(m(t),t%256); |
|
e[t]=o; |
|
e[i]=o; |
|
e[a.lower(i)]=o; |
|
end |
|
return e; |
|
end |
|
t.types={ |
|
'A','NS','MD','MF','CNAME','SOA','MB','MG','MR','NULL','WKS', |
|
'PTR','HINFO','MINFO','MX','TXT', |
|
[28]='AAAA',[29]='LOC',[33]='SRV', |
|
[252]='AXFR',[253]='MAILB',[254]='MAILA',[255]='*'}; |
|
t.classes={'IN','CS','CH','HS',[255]='*'}; |
|
t.type=w(t.types); |
|
t.class=w(t.classes); |
|
t.typecode=y(t.types); |
|
t.classcode=y(t.classes); |
|
local function g(e,i,o) |
|
if a.byte(e,-1)~=46 then e=e..'.';end |
|
e=a.lower(e); |
|
return e,t.type[i or'A'],t.class[o or'IN']; |
|
end |
|
local function y(t,a,o) |
|
a=a or s.gettime(); |
|
for n,e in c(t)do |
|
if e.tod then |
|
e.ttl=b.floor(e.tod-a); |
|
if e.ttl<=0 then |
|
t[e[e.type:lower()]]=nil; |
|
i.remove(t,n); |
|
return y(t,a,o); |
|
end |
|
elseif o=='soft'then |
|
k(e.ttl==0); |
|
t[e[e.type:lower()]]=nil; |
|
i.remove(t,n); |
|
end |
|
end |
|
end |
|
local e={}; |
|
e.__index=e; |
|
e.timeout=E; |
|
local function w(e) |
|
local e=e.type and e[e.type:lower()]; |
|
if q(e)~="string"then |
|
return"<UNKNOWN RDATA TYPE>"; |
|
end |
|
return e; |
|
end |
|
local k={ |
|
LOC=e.LOC_tostring; |
|
MX=function(e) |
|
return a.format('%2i %s',e.pref,e.mx); |
|
end; |
|
SRV=function(e) |
|
local e=e.srv; |
|
return a.format('%5d %5d %5d %s',e.priority,e.weight,e.port,e.target); |
|
end; |
|
}; |
|
local q={}; |
|
function q.__tostring(e) |
|
local t=(k[e.type]or w)(e); |
|
return a.format('%2s %-5s %6i %-28s %s',e.class,e.type,e.ttl,e.name,t); |
|
end |
|
local k={}; |
|
function k.__tostring(t) |
|
local e={}; |
|
for a,t in c(t)do |
|
h(e,p(t)..'\n'); |
|
end |
|
return i.concat(e); |
|
end |
|
local w={}; |
|
function w.__tostring(t) |
|
local a=s.gettime(); |
|
local e={}; |
|
for i,t in o(t)do |
|
for i,t in o(t)do |
|
for o,t in o(t)do |
|
y(t,a); |
|
h(e,p(t)); |
|
end |
|
end |
|
end |
|
return i.concat(e); |
|
end |
|
function e:new() |
|
local t={active={},cache={},unsorted={}}; |
|
r(t,e); |
|
r(t.cache,w); |
|
r(t.unsorted,{__mode='kv'}); |
|
return t; |
|
end |
|
function t.random(...) |
|
b.randomseed(b.floor(1e4*s.gettime())%2147483648); |
|
t.random=b.random; |
|
return t.random(...); |
|
end |
|
local function E(e) |
|
e=e or{}; |
|
e.id=e.id or t.random(0,65535); |
|
e.rd=e.rd or 1; |
|
e.tc=e.tc or 0; |
|
e.aa=e.aa or 0; |
|
e.opcode=e.opcode or 0; |
|
e.qr=e.qr or 0; |
|
e.rcode=e.rcode or 0; |
|
e.z=e.z or 0; |
|
e.ra=e.ra or 0; |
|
e.qdcount=e.qdcount or 1; |
|
e.ancount=e.ancount or 0; |
|
e.nscount=e.nscount or 0; |
|
e.arcount=e.arcount or 0; |
|
local t=a.char( |
|
m(e.id),e.id%256, |
|
e.rd+2*e.tc+4*e.aa+8*e.opcode+128*e.qr, |
|
e.rcode+16*e.z+128*e.ra, |
|
m(e.qdcount),e.qdcount%256, |
|
m(e.ancount),e.ancount%256, |
|
m(e.nscount),e.nscount%256, |
|
m(e.arcount),e.arcount%256 |
|
); |
|
return t,e.id; |
|
end |
|
local function m(t) |
|
local e={}; |
|
for t in a.gmatch(t,'[^.]+')do |
|
h(e,a.char(a.len(t))); |
|
h(e,t); |
|
end |
|
h(e,a.char(0)); |
|
return i.concat(e); |
|
end |
|
local function b(o,a,e) |
|
o=m(o); |
|
a=t.typecode[a or'a']; |
|
e=t.classcode[e or'in']; |
|
return o..a..e; |
|
end |
|
function e:byte(e) |
|
e=e or 1; |
|
local o=self.offset; |
|
local t=o+e-1; |
|
if t>#self.packet then |
|
x(a.format('out of bounds: %i>%i',t,#self.packet)); |
|
end |
|
self.offset=o+e; |
|
return a.byte(self.packet,o,t); |
|
end |
|
function e:word() |
|
local t,e=self:byte(2); |
|
return 256*t+e; |
|
end |
|
function e:dword() |
|
local o,t,a,e=self:byte(4); |
|
return 16777216*o+65536*t+256*a+e; |
|
end |
|
function e:sub(e) |
|
e=e or 1; |
|
local t=a.sub(self.packet,self.offset,self.offset+e-1); |
|
self.offset=self.offset+e; |
|
return t; |
|
end |
|
function e:header(t) |
|
local e=self:word(); |
|
if not self.active[e]and not t then return nil;end |
|
local e={id=e}; |
|
local t,a=self:byte(2); |
|
e.rd=t%2; |
|
e.tc=t/2%2; |
|
e.aa=t/4%2; |
|
e.opcode=t/8%16; |
|
e.qr=t/128; |
|
e.rcode=a%16; |
|
e.z=a/16%8; |
|
e.ra=a/128; |
|
e.qdcount=self:word(); |
|
e.ancount=self:word(); |
|
e.nscount=self:word(); |
|
e.arcount=self:word(); |
|
for a,t in o(e)do e[a]=t-t%1;end |
|
return e; |
|
end |
|
function e:name() |
|
local a,t=nil,0; |
|
local e=self:byte(); |
|
local o={}; |
|
if e==0 then return"."end |
|
while e>0 do |
|
if e>=192 then |
|
t=t+1; |
|
if t>=20 then x('dns error: 20 pointers');end; |
|
local e=((e-192)*256)+self:byte(); |
|
a=a or self.offset; |
|
self.offset=e+1; |
|
else |
|
h(o,self:sub(e)..'.'); |
|
end |
|
e=self:byte(); |
|
end |
|
self.offset=a or self.offset; |
|
return i.concat(o); |
|
end |
|
function e:question() |
|
local e={}; |
|
e.name=self:name(); |
|
e.type=t.type[self:word()]; |
|
e.class=t.class[self:word()]; |
|
return e; |
|
end |
|
function e:A(e) |
|
local o,t,n,i=self:byte(4); |
|
e.a=a.format('%i.%i.%i.%i',o,t,n,i); |
|
end |
|
function e:AAAA(a) |
|
local e={}; |
|
for t=1,a.rdlength,2 do |
|
local a,t=self:byte(2); |
|
i.insert(e,("%02x%02x"):format(a,t)); |
|
end |
|
e=i.concat(e,":"):gsub("%f[%x]0+(%x)","%1"); |
|
local t={}; |
|
for e in e:gmatch(":[0:]+:")do |
|
i.insert(t,e) |
|
end |
|
if#t==0 then |
|
a.aaaa=e; |
|
return |
|
elseif#t>1 then |
|
i.sort(t,function(t,e)return#t>#e end); |
|
end |
|
a.aaaa=e:gsub(t[1],"::",1):gsub("^0::","::"):gsub("::0$","::"); |
|
end |
|
function e:CNAME(e) |
|
e.cname=self:name(); |
|
end |
|
function e:MX(e) |
|
e.pref=self:word(); |
|
e.mx=self:name(); |
|
end |
|
function e:LOC_nibble_power() |
|
local e=self:byte(); |
|
return((e-(e%16))/16)*(10^(e%16)); |
|
end |
|
function e:LOC(e) |
|
e.version=self:byte(); |
|
if e.version==0 then |
|
e.loc=e.loc or{}; |
|
e.loc.size=self:LOC_nibble_power(); |
|
e.loc.horiz_pre=self:LOC_nibble_power(); |
|
e.loc.vert_pre=self:LOC_nibble_power(); |
|
e.loc.latitude=self:dword(); |
|
e.loc.longitude=self:dword(); |
|
e.loc.altitude=self:dword(); |
|
end |
|
end |
|
local function m(e,i,t) |
|
e=e-2147483648; |
|
if e<0 then i=t;e=-e;end |
|
local n,t,o; |
|
o=e%6e4; |
|
e=(e-o)/6e4; |
|
t=e%60; |
|
n=(e-t)/60; |
|
return a.format('%3d %2d %2.3f %s',n,t,o/1e3,i); |
|
end |
|
function e.LOC_tostring(e) |
|
local t={}; |
|
h(t,a.format( |
|
'%s %s %.2fm %.2fm %.2fm %.2fm', |
|
m(e.loc.latitude,'N','S'), |
|
m(e.loc.longitude,'E','W'), |
|
(e.loc.altitude-1e7)/100, |
|
e.loc.size/100, |
|
e.loc.horiz_pre/100, |
|
e.loc.vert_pre/100 |
|
)); |
|
return i.concat(t); |
|
end |
|
function e:NS(e) |
|
e.ns=self:name(); |
|
end |
|
function e:SOA(e) |
|
end |
|
function e:SRV(e) |
|
e.srv={}; |
|
e.srv.priority=self:word(); |
|
e.srv.weight=self:word(); |
|
e.srv.port=self:word(); |
|
e.srv.target=self:name(); |
|
end |
|
function e:PTR(e) |
|
e.ptr=self:name(); |
|
end |
|
function e:TXT(e) |
|
e.txt=self:sub(self:byte()); |
|
end |
|
function e:rr() |
|
local e={}; |
|
r(e,q); |
|
e.name=self:name(self); |
|
e.type=t.type[self:word()]or e.type; |
|
e.class=t.class[self:word()]or e.class; |
|
e.ttl=65536*self:word()+self:word(); |
|
e.rdlength=self:word(); |
|
if e.ttl<=0 then |
|
e.tod=self.time+30; |
|
else |
|
e.tod=self.time+e.ttl; |
|
end |
|
local a=self.offset; |
|
local t=self[t.type[e.type]]; |
|
if t then t(self,e);end |
|
self.offset=a; |
|
e.rdata=self:sub(e.rdlength); |
|
return e; |
|
end |
|
function e:rrs(t) |
|
local e={}; |
|
for t=1,t do h(e,self:rr());end |
|
return e; |
|
end |
|
function e:decode(t,o) |
|
self.packet,self.offset=t,1; |
|
local t=self:header(o); |
|
if not t then return nil;end |
|
local t={header=t}; |
|
t.question={}; |
|
local i=self.offset; |
|
for e=1,t.header.qdcount do |
|
h(t.question,self:question()); |
|
end |
|
t.question.raw=a.sub(self.packet,i,self.offset-1); |
|
if not o then |
|
if not self.active[t.header.id]or not self.active[t.header.id][t.question.raw]then |
|
self.active[t.header.id]=nil; |
|
return nil; |
|
end |
|
end |
|
t.answer=self:rrs(t.header.ancount); |
|
t.authority=self:rrs(t.header.nscount); |
|
t.additional=self:rrs(t.header.arcount); |
|
return t; |
|
end |
|
e.delays={1,3}; |
|
function e:addnameserver(e) |
|
self.server=self.server or{}; |
|
h(self.server,e); |
|
end |
|
function e:setnameserver(e) |
|
self.server={}; |
|
self:addnameserver(e); |
|
end |
|
function e:adddefaultnameservers() |
|
if _ then |
|
if v and v.get_nameservers then |
|
for t,e in c(v.get_nameservers())do |
|
self:addnameserver(e); |
|
end |
|
end |
|
if not self.server or#self.server==0 then |
|
self:addnameserver("208.67.222.222"); |
|
self:addnameserver("208.67.220.220"); |
|
end |
|
else |
|
local e=z.open("/etc/resolv.conf"); |
|
if e then |
|
for e in e:lines()do |
|
e=e:gsub("#.*$","") |
|
:match('^%s*nameserver%s+(.*)%s*$'); |
|
if e then |
|
e:gsub("%f[%d.](%d+%.%d+%.%d+%.%d+)%f[^%d.]",function(e) |
|
self:addnameserver(e) |
|
end); |
|
end |
|
end |
|
end |
|
if not self.server or#self.server==0 then |
|
self:addnameserver("127.0.0.1"); |
|
end |
|
end |
|
end |
|
function e:getsocket(a) |
|
self.socket=self.socket or{}; |
|
self.socketset=self.socketset or{}; |
|
local e=self.socket[a]; |
|
if e then return e;end |
|
local o,t; |
|
e,t=s.udp(); |
|
if e and self.socket_wrapper then e,t=self.socket_wrapper(e,self);end |
|
if not e then |
|
return nil,t; |
|
end |
|
e:settimeout(0); |
|
self.socket[a]=e; |
|
self.socketset[e]=a; |
|
o,t=e:setsockname('*',0); |
|
if not o then return self:servfail(e,t);end |
|
o,t=e:setpeername(self.server[a],53); |
|
if not o then return self:servfail(e,t);end |
|
return e; |
|
end |
|
function e:voidsocket(e) |
|
if self.socket[e]then |
|
self.socketset[self.socket[e]]=nil; |
|
self.socket[e]=nil; |
|
elseif self.socketset[e]then |
|
self.socket[self.socketset[e]]=nil; |
|
self.socketset[e]=nil; |
|
end |
|
e:close(); |
|
end |
|
function e:socket_wrapper_set(e) |
|
self.socket_wrapper=e; |
|
end |
|
function e:closeall() |
|
for t,e in c(self.socket)do |
|
self.socket[t]=nil; |
|
self.socketset[e]=nil; |
|
e:close(); |
|
end |
|
end |
|
function e:remember(e,t) |
|
local i,o,a=g(e.name,e.type,e.class); |
|
if t~='*'then |
|
t=o; |
|
local t=d(self.cache,a,'*',i); |
|
if t then h(t,e);end |
|
end |
|
self.cache=self.cache or r({},w); |
|
local a=d(self.cache,a,t,i)or |
|
l(self.cache,a,t,i,r({},k)); |
|
if not a[e[o:lower()]]then |
|
a[e[o:lower()]]=true; |
|
h(a,e); |
|
end |
|
if t=='MX'then self.unsorted[a]=true;end |
|
end |
|
local function m(e,t) |
|
return(e.pref==t.pref)and(e.mx<t.mx)or(e.pref<t.pref); |
|
end |
|
function e:peek(o,a,t,h) |
|
o,a,t=g(o,a,t); |
|
local e=d(self.cache,t,a,o); |
|
if not e then |
|
if h then if h<=0 then return end else h=3 end |
|
e=d(self.cache,t,"CNAME",o); |
|
if not(e and e[1])then return end |
|
return self:peek(e[1].cname,a,t,h-1); |
|
end |
|
if y(e,s.gettime())and a=='*'or not n(e)then |
|
l(self.cache,t,a,o,nil); |
|
return nil; |
|
end |
|
if self.unsorted[e]then i.sort(e,m);self.unsorted[e]=nil;end |
|
return e; |
|
end |
|
function e:purge(e) |
|
if e=='soft'then |
|
self.time=s.gettime(); |
|
for t,e in o(self.cache or{})do |
|
for t,e in o(e)do |
|
for t,e in o(e)do |
|
y(e,self.time,'soft') |
|
end |
|
end |
|
end |
|
else self.cache=r({},w);end |
|
end |
|
function e:query(e,t,a) |
|
e,t,a=g(e,t,a) |
|
local n=u.running(); |
|
local o=d(self.wanted,a,t,e); |
|
if n and o then |
|
l(self.wanted,a,t,e,n,true); |
|
return true; |
|
end |
|
if not self.server then self:adddefaultnameservers();end |
|
local h=b(e,t,a); |
|
local o=self:peek(e,t,a); |
|
if o then return o;end |
|
local i,o=E(); |
|
local i={ |
|
packet=i..h, |
|
server=self.best_server, |
|
delay=1, |
|
retry=s.gettime()+self.delays[1] |
|
}; |
|
self.active[o]=self.active[o]or{}; |
|
self.active[o][h]=i; |
|
if n then |
|
l(self.wanted,a,t,e,n,true); |
|
end |
|
local o,h=self:getsocket(i.server) |
|
if not o then |
|
return nil,h; |
|
end |
|
o:send(i.packet) |
|
if j and self.timeout then |
|
local r=#self.server; |
|
local s=1; |
|
j.add_task(self.timeout,function() |
|
if d(self.wanted,a,t,e,n)then |
|
if s<r then |
|
s=s+1; |
|
self:servfail(o); |
|
i.server=self.best_server; |
|
o,h=self:getsocket(i.server); |
|
if o then |
|
o:send(i.packet); |
|
return self.timeout; |
|
end |
|
end |
|
self:cancel(a,t,e); |
|
end |
|
end) |
|
end |
|
return true; |
|
end |
|
function e:servfail(t,i) |
|
local h=self.socketset[t] |
|
t=self:voidsocket(t); |
|
self.time=s.gettime(); |
|
for s,a in o(self.active)do |
|
for o,e in o(a)do |
|
if e.server==h then |
|
e.server=e.server+1 |
|
if e.server>#self.server then |
|
e.server=1; |
|
end |
|
e.retries=(e.retries or 0)+1; |
|
if e.retries>=#self.server then |
|
a[o]=nil; |
|
else |
|
t,i=self:getsocket(e.server); |
|
if t then t:send(e.packet);end |
|
end |
|
end |
|
end |
|
if n(a)==nil then |
|
self.active[s]=nil; |
|
end |
|
end |
|
if h==self.best_server then |
|
self.best_server=self.best_server+1; |
|
if self.best_server>#self.server then |
|
self.best_server=1; |
|
end |
|
end |
|
return t,i; |
|
end |
|
function e:settimeout(e) |
|
self.timeout=e; |
|
end |
|
function e:receive(t) |
|
self.time=s.gettime(); |
|
t=t or self.socket; |
|
local e; |
|
for a,t in o(t)do |
|
if self.socketset[t]then |
|
local t=t:receive(); |
|
if t then |
|
e=self:decode(t); |
|
if e and self.active[e.header.id] |
|
and self.active[e.header.id][e.question.raw]then |
|
for a,t in o(e.answer)do |
|
self:remember(t,e.question[1].type) |
|
end |
|
local t=self.active[e.header.id]; |
|
t[e.question.raw]=nil; |
|
if not n(t)then self.active[e.header.id]=nil;end |
|
if not n(self.active)then self:closeall();end |
|
local e=e.question[1]; |
|
local t=d(self.wanted,e.class,e.type,e.name); |
|
if t then |
|
for e in o(t)do |
|
if u.status(e)=="suspended"then u.resume(e);end |
|
end |
|
l(self.wanted,e.class,e.type,e.name,nil); |
|
end |
|
end |
|
end |
|
end |
|
end |
|
return e; |
|
end |
|
function e:feed(a,e,t) |
|
self.time=s.gettime(); |
|
local e=self:decode(e,t); |
|
if e and self.active[e.header.id] |
|
and self.active[e.header.id][e.question.raw]then |
|
for a,t in o(e.answer)do |
|
self:remember(t,e.question[1].type); |
|
end |
|
local t=self.active[e.header.id]; |
|
t[e.question.raw]=nil; |
|
if not n(t)then self.active[e.header.id]=nil;end |
|
if not n(self.active)then self:closeall();end |
|
local e=e.question[1]; |
|
if e then |
|
local t=d(self.wanted,e.class,e.type,e.name); |
|
if t then |
|
for e in o(t)do |
|
if u.status(e)=="suspended"then u.resume(e);end |
|
end |
|
l(self.wanted,e.class,e.type,e.name,nil); |
|
end |
|
end |
|
end |
|
return e; |
|
end |
|
function e:cancel(i,t,e) |
|
local a=d(self.wanted,i,t,e); |
|
if a then |
|
for e in o(a)do |
|
if u.status(e)=="suspended"then u.resume(e);end |
|
end |
|
l(self.wanted,i,t,e,nil); |
|
end |
|
end |
|
function e:pulse() |
|
while self:receive()do end |
|
if not n(self.active)then return nil;end |
|
self.time=s.gettime(); |
|
for i,t in o(self.active)do |
|
for a,e in o(t)do |
|
if self.time>=e.retry then |
|
e.server=e.server+1; |
|
if e.server>#self.server then |
|
e.server=1; |
|
e.delay=e.delay+1; |
|
end |
|
if e.delay>#self.delays then |
|
t[a]=nil; |
|
if not n(t)then self.active[i]=nil;end |
|
if not n(self.active)then return nil;end |
|
else |
|
local t=self.socket[e.server]; |
|
if t then t:send(e.packet);end |
|
e.retry=self.time+self.delays[e.delay]; |
|
end |
|
end |
|
end |
|
end |
|
if n(self.active)then return true;end |
|
return nil; |
|
end |
|
function e:lookup(t,o,a) |
|
self:query(t,o,a) |
|
while self:pulse()do |
|
local e={} |
|
for a,t in c(self.socket)do |
|
e[a]=t |
|
end |
|
s.select(e,nil,4) |
|
end |
|
return self:peek(t,o,a); |
|
end |
|
function e:lookupex(o,e,t,a) |
|
return self:peek(e,t,a)or self:query(e,t,a); |
|
end |
|
function e:tohostname(e) |
|
return t.lookup(e:gsub("(%d+)%.(%d+)%.(%d+)%.(%d+)","%4.%3.%2.%1.in-addr.arpa."),"PTR"); |
|
end |
|
local i={ |
|
qr={[0]='query','response'}, |
|
opcode={[0]='query','inverse query','server status request'}, |
|
aa={[0]='non-authoritative','authoritative'}, |
|
tc={[0]='complete','truncated'}, |
|
rd={[0]='recursion not desired','recursion desired'}, |
|
ra={[0]='recursion not available','recursion available'}, |
|
z={[0]='(reserved)'}, |
|
rcode={[0]='no error','format error','server failure','name error','not implemented'}, |
|
type=t.type, |
|
class=t.class |
|
}; |
|
local function n(t,e) |
|
return(i[e]and i[e][t[e]])or''; |
|
end |
|
function e.print(e) |
|
for o,t in o{'id','qr','opcode','aa','tc','rd','ra','z', |
|
'rcode','qdcount','ancount','nscount','arcount'}do |
|
f(a.format('%-30s','header.'..t),e.header[t],n(e.header,t)); |
|
end |
|
for t,e in c(e.question)do |
|
f(a.format('question[%i].name ',t),e.name); |
|
f(a.format('question[%i].type ',t),e.type); |
|
f(a.format('question[%i].class ',t),e.class); |
|
end |
|
local h={name=1,type=1,class=1,ttl=1,rdlength=1,rdata=1}; |
|
local t; |
|
for s,i in o({'answer','authority','additional'})do |
|
for s,e in o(e[i])do |
|
for h,o in o({'name','type','class','ttl','rdlength'})do |
|
t=a.format('%s[%i].%s',i,s,o); |
|
f(a.format('%-30s',t),e[o],n(e,o)); |
|
end |
|
for e,o in o(e)do |
|
if not h[e]then |
|
t=a.format('%s[%i].%s',i,s,e); |
|
f(a.format('%-30s %s',p(t),p(o))); |
|
end |
|
end |
|
end |
|
end |
|
end |
|
function t.resolver() |
|
local t={active={},cache={},unsorted={},wanted={},best_server=1}; |
|
r(t,e); |
|
r(t.cache,w); |
|
r(t.unsorted,{__mode='kv'}); |
|
return t; |
|
end |
|
local e=t.resolver(); |
|
t._resolver=e; |
|
function t.lookup(...) |
|
return e:lookup(...); |
|
end |
|
function t.tohostname(...) |
|
return e:tohostname(...); |
|
end |
|
function t.purge(...) |
|
return e:purge(...); |
|
end |
|
function t.peek(...) |
|
return e:peek(...); |
|
end |
|
function t.query(...) |
|
return e:query(...); |
|
end |
|
function t.feed(...) |
|
return e:feed(...); |
|
end |
|
function t.cancel(...) |
|
return e:cancel(...); |
|
end |
|
function t.settimeout(...) |
|
return e:settimeout(...); |
|
end |
|
function t.cache() |
|
return e.cache; |
|
end |
|
function t.socket_wrapper_set(...) |
|
return e:socket_wrapper_set(...); |
|
end |
|
return t; |
|
end) |
|
package.preload['net.adns']=(function(...) |
|
local c=require"net.server"; |
|
local a=require"net.dns"; |
|
local e=require"util.logger".init("adns"); |
|
local t,t=table.insert,table.remove; |
|
local o,r,l=coroutine,tostring,pcall; |
|
local function u(a,a,e,t)return(t-e)+1;end |
|
module"adns" |
|
function lookup(d,t,h,s) |
|
return o.wrap(function(i) |
|
if i then |
|
e("debug","Records for %s already cached, using those...",t); |
|
d(i); |
|
return; |
|
end |
|
e("debug","Records for %s not in cache, sending query (%s)...",t,r(o.running())); |
|
local i,n=a.query(t,h,s); |
|
if i then |
|
o.yield({s or"IN",h or"A",t,o.running()}); |
|
e("debug","Reply for %s (%s)",t,r(o.running())); |
|
end |
|
if i then |
|
i,n=l(d,a.peek(t,h,s)); |
|
else |
|
e("error","Error sending DNS query: %s",n); |
|
i,n=l(d,nil,n); |
|
end |
|
if not i then |
|
e("error","Error in DNS response handler: %s",r(n)); |
|
end |
|
end)(a.peek(t,h,s)); |
|
end |
|
function cancel(t,o,i) |
|
e("warn","Cancelling DNS lookup for %s",r(t[3])); |
|
a.cancel(t[1],t[2],t[3],t[4],o); |
|
end |
|
function new_async_socket(o,i) |
|
local n="<unknown>"; |
|
local s={}; |
|
local t={}; |
|
local h; |
|
function s.onincoming(o,e) |
|
if e then |
|
a.feed(t,e); |
|
end |
|
end |
|
function s.ondisconnect(a,o) |
|
if o then |
|
e("warn","DNS socket for %s disconnected: %s",n,o); |
|
local t=i.server; |
|
if i.socketset[a]==i.best_server and i.best_server==#t then |
|
e("error","Exhausted all %d configured DNS servers, next lookup will try %s again",#t,t[1]); |
|
end |
|
i:servfail(a); |
|
end |
|
end |
|
t,h=c.wrapclient(o,"dns",53,s); |
|
if not t then |
|
return nil,h; |
|
end |
|
t.settimeout=function()end |
|
t.setsockname=function(e,...)return o:setsockname(...);end |
|
t.setpeername=function(i,...)n=(...);local a,e=o:setpeername(...);i:set_send(u);return a,e;end |
|
t.connect=function(e,...)return o:connect(...)end |
|
t.send=function(a,t) |
|
e("debug","Sending DNS query to %s",n); |
|
return o:send(t); |
|
end |
|
return t; |
|
end |
|
a.socket_wrapper_set(new_async_socket); |
|
return _M; |
|
end) |
|
package.preload['net.server']=(function(...) |
|
local l=function(e) |
|
return _G[e] |
|
end |
|
local M,e=require("util.logger").init("socket"),table.concat; |
|
local i=function(...)return M("debug",e{...});end |
|
local H=function(...)return M("warn",e{...});end |
|
local ce=1 |
|
local F=l"type" |
|
local W=l"pairs" |
|
local de=l"ipairs" |
|
local y=l"tonumber" |
|
local h=l"tostring" |
|
local t=l"os" |
|
local o=l"table" |
|
local a=l"string" |
|
local e=l"coroutine" |
|
local Z=t.difftime |
|
local X=math.min |
|
local re=math.huge |
|
local fe=o.concat |
|
local me=a.sub |
|
local we=e.wrap |
|
local ye=e.yield |
|
local x=l"ssl" |
|
local b=l"socket"or require"socket" |
|
local J=b.gettime |
|
local ve=(x and x.wrap) |
|
local he=b.bind |
|
local pe=b.sleep |
|
local be=b.select |
|
local G |
|
local B |
|
local ae |
|
local K |
|
local te |
|
local c |
|
local ee |
|
local oe |
|
local ne |
|
local ie |
|
local se |
|
local Q |
|
local r |
|
local le |
|
local D |
|
local ue |
|
local p |
|
local s |
|
local R |
|
local d |
|
local n |
|
local S |
|
local g |
|
local f |
|
local m |
|
local a |
|
local o |
|
local v |
|
local U |
|
local C |
|
local T |
|
local E |
|
local k |
|
local V |
|
local u |
|
local _ |
|
local z |
|
local A |
|
local I |
|
local O |
|
local L |
|
local j |
|
local q |
|
local N |
|
p={} |
|
s={} |
|
d={} |
|
R={} |
|
n={} |
|
g={} |
|
f={} |
|
S={} |
|
a=0 |
|
o=0 |
|
v=0 |
|
U=0 |
|
C=0 |
|
T=1 |
|
E=0 |
|
k=128 |
|
_=51e3*1024 |
|
z=25e3*1024 |
|
A=12e5 |
|
I=6e4 |
|
O=6*60*60 |
|
local e=package.config:sub(1,1)=="\\" |
|
q=(e and math.huge)or b._SETSIZE or 1024 |
|
j=b._SETSIZE or 1024 |
|
N=30 |
|
ie=function(w,t,f,l,v,u) |
|
if t:getfd()>=q then |
|
H("server.lua: Disallowed FD number: "..t:getfd()) |
|
t:close() |
|
return nil,"fd-too-large" |
|
end |
|
local m=0 |
|
local y,e=w.onconnect,w.ondisconnect |
|
local b=t.accept |
|
local e={} |
|
e.shutdown=function()end |
|
e.ssl=function() |
|
return u~=nil |
|
end |
|
e.sslctx=function() |
|
return u |
|
end |
|
e.remove=function() |
|
m=m-1 |
|
if e then |
|
e.resume() |
|
end |
|
end |
|
e.close=function() |
|
t:close() |
|
o=r(d,t,o) |
|
a=r(s,t,a) |
|
p[f..":"..l]=nil; |
|
n[t]=nil |
|
e=nil |
|
t=nil |
|
i"server.lua: closed server handler and removed sockets from list" |
|
end |
|
e.pause=function(o) |
|
if not e.paused then |
|
a=r(s,t,a) |
|
if o then |
|
n[t]=nil |
|
t:close() |
|
t=nil; |
|
end |
|
e.paused=true; |
|
end |
|
end |
|
e.resume=function() |
|
if e.paused then |
|
if not t then |
|
t=he(f,l,k); |
|
t:settimeout(0) |
|
end |
|
a=c(s,t,a) |
|
n[t]=e |
|
e.paused=false; |
|
end |
|
end |
|
e.ip=function() |
|
return f |
|
end |
|
e.serverport=function() |
|
return l |
|
end |
|
e.socket=function() |
|
return t |
|
end |
|
e.readbuffer=function() |
|
if a>=j or o>=j then |
|
e.pause() |
|
i("server.lua: refused new client connection: server full") |
|
return false |
|
end |
|
local t,o=b(t) |
|
if t then |
|
local o,a=t:getpeername() |
|
local t,n,e=D(e,w,t,o,l,a,v,u) |
|
if e then |
|
return false |
|
end |
|
m=m+1 |
|
i("server.lua: accepted new client connection from ",h(o),":",h(a)," to ",h(l)) |
|
if y and not u then |
|
return y(t); |
|
end |
|
return; |
|
elseif o then |
|
i("server.lua: error with new client connection: ",h(o)) |
|
return false |
|
end |
|
end |
|
return e |
|
end |
|
D=function(E,y,t,I,Q,O,T,j) |
|
if t:getfd()>=q then |
|
H("server.lua: Disallowed FD number: "..t:getfd()) |
|
t:close() |
|
if E then |
|
E.pause() |
|
end |
|
return nil,nil,"fd-too-large" |
|
end |
|
t:settimeout(0) |
|
local p |
|
local A |
|
local k |
|
local P |
|
local W=y.onincoming |
|
local Y=y.onstatus |
|
local b=y.ondisconnect |
|
local F=y.ondrain |
|
local M=y.ondetach |
|
local v={} |
|
local l=0 |
|
local V |
|
local B |
|
local L |
|
local w=0 |
|
local q=false |
|
local H=false |
|
local D,R=0,0 |
|
local _=_ |
|
local z=z |
|
local e=v |
|
e.dispatch=function() |
|
return W |
|
end |
|
e.disconnect=function() |
|
return b |
|
end |
|
e.setlistener=function(a,t) |
|
if M then |
|
M(a) |
|
end |
|
W=t.onincoming |
|
b=t.ondisconnect |
|
Y=t.onstatus |
|
F=t.ondrain |
|
M=t.ondetach |
|
end |
|
e.getstats=function() |
|
return R,D |
|
end |
|
e.ssl=function() |
|
return P |
|
end |
|
e.sslctx=function() |
|
return j |
|
end |
|
e.send=function(n,o,i,a) |
|
return p(t,o,i,a) |
|
end |
|
e.receive=function(o,a) |
|
return A(t,o,a) |
|
end |
|
e.shutdown=function(a) |
|
return k(t,a) |
|
end |
|
e.setoption=function(i,a,o) |
|
if t.setoption then |
|
return t:setoption(a,o); |
|
end |
|
return false,"setoption not implemented"; |
|
end |
|
e.force_close=function(t,a) |
|
if l~=0 then |
|
i("server.lua: discarding unwritten data for ",h(I),":",h(O)) |
|
l=0; |
|
end |
|
return t:close(a); |
|
end |
|
e.close=function(u,h) |
|
if not e then return true;end |
|
a=r(s,t,a) |
|
g[e]=nil |
|
if l~=0 then |
|
e.sendbuffer() |
|
if l~=0 then |
|
if e then |
|
e.write=nil |
|
end |
|
V=true |
|
return false |
|
end |
|
end |
|
if t then |
|
m=k and k(t) |
|
t:close() |
|
o=r(d,t,o) |
|
n[t]=nil |
|
t=nil |
|
else |
|
i"server.lua: socket already closed" |
|
end |
|
if e then |
|
f[e]=nil |
|
S[e]=nil |
|
local t=e; |
|
e=nil |
|
if b then |
|
b(t,h or false); |
|
b=nil |
|
end |
|
end |
|
if E then |
|
E.remove() |
|
end |
|
i"server.lua: closed client handler and removed socket from list" |
|
return true |
|
end |
|
e.ip=function() |
|
return I |
|
end |
|
e.serverport=function() |
|
return Q |
|
end |
|
e.clientport=function() |
|
return O |
|
end |
|
e.port=e.clientport |
|
local b=function(i,a) |
|
w=w+#a |
|
if w>_ then |
|
S[e]="send buffer exceeded" |
|
e.write=K |
|
return false |
|
elseif t and not d[t]then |
|
o=c(d,t,o) |
|
end |
|
l=l+1 |
|
v[l]=a |
|
if e then |
|
f[e]=f[e]or u |
|
end |
|
return true |
|
end |
|
e.write=b |
|
e.bufferqueue=function(t) |
|
return v |
|
end |
|
e.socket=function(a) |
|
return t |
|
end |
|
e.set_mode=function(a,t) |
|
T=t or T |
|
return T |
|
end |
|
e.set_send=function(a,t) |
|
p=t or p |
|
return p |
|
end |
|
e.bufferlen=function(o,a,t) |
|
_=t or _ |
|
z=a or z |
|
return w,z,_ |
|
end |
|
e.lock_read=function(i,o) |
|
if o==true then |
|
local o=a |
|
a=r(s,t,a) |
|
g[e]=nil |
|
if a~=o then |
|
q=true |
|
end |
|
elseif o==false then |
|
if q then |
|
q=false |
|
a=c(s,t,a) |
|
g[e]=u |
|
end |
|
end |
|
return q |
|
end |
|
e.pause=function(t) |
|
return t:lock_read(true); |
|
end |
|
e.resume=function(t) |
|
return t:lock_read(false); |
|
end |
|
e.lock=function(i,a) |
|
e.lock_read(a) |
|
if a==true then |
|
e.write=K |
|
local a=o |
|
o=r(d,t,o) |
|
f[e]=nil |
|
if o~=a then |
|
H=true |
|
end |
|
elseif a==false then |
|
e.write=b |
|
if H then |
|
H=false |
|
b("") |
|
end |
|
end |
|
return q,H |
|
end |
|
local b=function() |
|
local a,t,o=A(t,T) |
|
if not t or(t=="wantread"or t=="timeout")then |
|
local o=a or o or"" |
|
local a=#o |
|
if a>z then |
|
e:close("receive buffer exceeded") |
|
return false |
|
end |
|
local a=a*ce |
|
R=R+a |
|
C=C+a |
|
g[e]=u |
|
return W(e,o,t) |
|
else |
|
i("server.lua: client ",h(I),":",h(O)," read error: ",h(t)) |
|
B=true |
|
m=e and e:force_close(t) |
|
return false |
|
end |
|
end |
|
local w=function() |
|
local c,a,n,s,y; |
|
if t then |
|
s=fe(v,"",1,l) |
|
c,a,n=p(t,s,1,w) |
|
y=(c or n or 0)*ce |
|
D=D+y |
|
U=U+y |
|
for e=l,1,-1 do |
|
v[e]=nil |
|
end |
|
else |
|
c,a,y=false,"unexpected close",0; |
|
end |
|
if c then |
|
l=0 |
|
w=0 |
|
o=r(d,t,o) |
|
f[e]=nil |
|
if F then |
|
F(e) |
|
end |
|
m=L and e:starttls(nil) |
|
m=V and e:force_close() |
|
return true |
|
elseif n and(a=="timeout"or a=="wantwrite")then |
|
s=me(s,n+1,w) |
|
v[1]=s |
|
l=1 |
|
w=w-n |
|
f[e]=u |
|
return true |
|
else |
|
i("server.lua: client ",h(I),":",h(O)," write error: ",h(a)) |
|
B=true |
|
m=e and e:force_close(a) |
|
return false |
|
end |
|
end |
|
local u; |
|
function e.set_sslctx(p,t) |
|
j=t; |
|
local l,f |
|
u=we(function(n) |
|
local t |
|
for h=1,N do |
|
o=(f and r(d,n,o))or o |
|
a=(l and r(s,n,a))or a |
|
l,f=nil,nil |
|
m,t=n:dohandshake() |
|
if not t then |
|
i("server.lua: ssl handshake done") |
|
e.readbuffer=b |
|
e.sendbuffer=w |
|
m=Y and Y(e,"ssl-handshake-complete") |
|
if p.autostart_ssl and y.onconnect then |
|
y.onconnect(p); |
|
end |
|
a=c(s,n,a) |
|
return true |
|
else |
|
if t=="wantwrite"then |
|
o=c(d,n,o) |
|
f=true |
|
elseif t=="wantread"then |
|
a=c(s,n,a) |
|
l=true |
|
else |
|
break; |
|
end |
|
t=nil; |
|
ye() |
|
end |
|
end |
|
i("server.lua: ssl handshake error: ",h(t or"handshake too long")) |
|
m=e and e:force_close("ssl handshake failed") |
|
return false,t |
|
end |
|
) |
|
end |
|
if x then |
|
e.starttls=function(f,m) |
|
if m then |
|
e:set_sslctx(m); |
|
end |
|
if l>0 then |
|
i"server.lua: we need to do tls, but delaying until send buffer empty" |
|
L=true |
|
return |
|
end |
|
i("server.lua: attempting to start tls on "..h(t)) |
|
local m,l=t |
|
t,l=ve(t,j) |
|
if not t then |
|
i("server.lua: error while starting tls on client: ",h(l or"unknown error")) |
|
return nil,l |
|
end |
|
t:settimeout(0) |
|
p=t.send |
|
A=t.receive |
|
k=G |
|
n[t]=e |
|
a=c(s,t,a) |
|
a=r(s,m,a) |
|
o=r(d,m,o) |
|
n[m]=nil |
|
e.starttls=nil |
|
L=nil |
|
P=true |
|
e.readbuffer=u |
|
e.sendbuffer=u |
|
return u(t) |
|
end |
|
end |
|
e.readbuffer=b |
|
e.sendbuffer=w |
|
p=t.send |
|
A=t.receive |
|
k=(P and G)or t.shutdown |
|
n[t]=e |
|
a=c(s,t,a) |
|
if j and x then |
|
i"server.lua: auto-starting ssl negotiation..." |
|
e.autostart_ssl=true; |
|
local t,e=e:starttls(j); |
|
if t==false then |
|
return nil,nil,e |
|
end |
|
end |
|
return e,t |
|
end |
|
G=function() |
|
end |
|
K=function() |
|
return false |
|
end |
|
c=function(t,a,e) |
|
if not t[a]then |
|
e=e+1 |
|
t[e]=a |
|
t[a]=e |
|
end |
|
return e; |
|
end |
|
r=function(e,a,t) |
|
local o=e[a] |
|
if o then |
|
e[a]=nil |
|
local i=e[t] |
|
e[t]=nil |
|
if i~=a then |
|
e[i]=o |
|
e[o]=i |
|
end |
|
return t-1 |
|
end |
|
return t |
|
end |
|
Q=function(e) |
|
o=r(d,e,o) |
|
a=r(s,e,a) |
|
n[e]=nil |
|
e:close() |
|
end |
|
local function w(e,a,o) |
|
local t; |
|
local i=a.sendbuffer; |
|
function a.sendbuffer() |
|
i(); |
|
if t and a.bufferlen()<o then |
|
e:lock_read(false); |
|
t=nil; |
|
end |
|
end |
|
local i=e.readbuffer; |
|
function e.readbuffer() |
|
i(); |
|
if not t and a.bufferlen()>=o then |
|