@@ -1,63 +0,0 @@ | |||
local imt = {} | |||
imt.ttypes = {} | |||
imt.ttypes.string=1 | |||
imt.ttypes.number=2 | |||
imt.ftypes = {tostring,tonumber} | |||
function imt.to16bn(n) | |||
return string.char(math.floor(n/256))..string.char(math.floor(n%256)) | |||
end | |||
function imt.from16bn(s) | |||
return (string.byte(s,1,1)*256)+string.byte(s,2,2) | |||
end | |||
function imt.encodePacket(...) | |||
local tArgs = {...} | |||
local packet = string.char(#tArgs%256) | |||
for _,segment in ipairs(tArgs) do | |||
local segtype = type(segment) | |||
segment = tostring(segment) | |||
packet = packet .. imt.to16bn(segment:len()) .. string.char(imt.ttypes[segtype]) .. tostring(segment) | |||
end | |||
packet = imt.to16bn(packet:len()) .. packet | |||
return packet | |||
end | |||
function imt.decodePacket(s) | |||
local function getfirst(n) | |||
local ns = s:sub(1,n) | |||
s=s:sub(n+1) | |||
return ns | |||
end | |||
if s:len() < 2 then return false end | |||
local plen = imt.from16bn(getfirst(2)) | |||
local segments = {} | |||
if s:len() < plen then return false end | |||
local nsegments = string.byte(getfirst(1)) | |||
--print(tostring(plen).." bytes, "..tostring(nsegments).." segments") | |||
for i = 1, nsegments do | |||
local seglen = imt.from16bn(getfirst(2)) | |||
local segtype = imt.ftypes[string.byte(getfirst(1))] | |||
local segment = segtype(getfirst(seglen)) | |||
--print(seglen,segtype,segment,type(segment)) | |||
segments[#segments+1] = segment | |||
end | |||
return table.unpack(segments) | |||
end | |||
function imt.getRemainder(s) | |||
local function getfirst(n) | |||
local ns = s:sub(1,n) | |||
s=s:sub(n+1) | |||
return ns | |||
end | |||
local plen = imt.from16bn(getfirst(2)) | |||
if s:len() > plen then | |||
getfirst(plen) | |||
return s | |||
end | |||
return nil | |||
end | |||
return imt |
@@ -1,197 +0,0 @@ | |||
local proxylist = {} | |||
local proxyobjs = {} | |||
local typelist = {} | |||
local doclist = {} | |||
local oproxy = component.proxy | |||
function component.proxy(address) | |||
checkArg(1,address,"string") | |||
if proxyobjs[address] ~= nil then | |||
return proxyobjs[address] | |||
end | |||
return oproxy(address) | |||
end | |||
local olist = component.list | |||
function component.list(filter, exact) | |||
checkArg(1,filter,"string","nil") | |||
local result = {} | |||
local data = {} | |||
for k,v in olist(filter, exact) do | |||
data[#data + 1] = k | |||
data[#data + 1] = v | |||
result[k] = v | |||
end | |||
for k,v in pairs(typelist) do | |||
if filter == nil or (exact and v == filter) or (not exact and v:find(filter, nil, true)) then | |||
data[#data + 1] = k | |||
data[#data + 1] = v | |||
result[k] = v | |||
end | |||
end | |||
local place = 1 | |||
return setmetatable(result, | |||
{__call=function() | |||
local addr,type = data[place], data[place + 1] | |||
place = place + 2 | |||
return addr, type | |||
end} | |||
) | |||
end | |||
local otype = component.type | |||
function component.type(address) | |||
checkArg(1,address,"string") | |||
if typelist[address] ~= nil then | |||
return typelist[address] | |||
end | |||
return otype(address) | |||
end | |||
local odoc = component.doc | |||
function component.doc(address, method) | |||
checkArg(1,address,"string") | |||
checkArg(2,method,"string") | |||
if proxylist[address] ~= nil then | |||
if proxylist[address][method] == nil then | |||
error("no such method",2) | |||
end | |||
if doclist[address] ~= nil then | |||
return doclist[address][method] | |||
end | |||
return nil | |||
end | |||
return odoc(address, method) | |||
end | |||
local oslot = component.slot | |||
function component.slot(address) | |||
checkArg(1,address,"string") | |||
if proxylist[address] ~= nil then | |||
return -1 -- vcomponents do not exist in a slot | |||
end | |||
return oslot(address) | |||
end | |||
local omethods = component.methods | |||
function component.methods(address) | |||
checkArg(1,address,"string") | |||
if proxylist[address] ~= nil then | |||
local methods = {} | |||
for k,v in pairs(proxylist[address]) do | |||
if type(v) == "function" then | |||
methods[k] = true -- All vcomponent methods are direct | |||
end | |||
end | |||
return methods | |||
end | |||
return omethods(address) | |||
end | |||
local oinvoke = component.invoke | |||
function component.invoke(address, method, ...) | |||
checkArg(1,address,"string") | |||
checkArg(2,method,"string") | |||
if proxylist[address] ~= nil then | |||
if proxylist[address][method] == nil then | |||
error("no such method",2) | |||
end | |||
return proxylist[address][method](...) | |||
end | |||
return oinvoke(address, method, ...) | |||
end | |||
local ofields = component.fields | |||
function component.fields(address) | |||
checkArg(1,address,"string") | |||
if proxylist[address] ~= nil then | |||
return {} -- What even is this? | |||
end | |||
return ofields(address) | |||
end | |||
local componentCallback = | |||
{ | |||
__call = function(self, ...) return proxylist[self.address][self.name](...) end, | |||
__tostring = function(self) return (doclist[self.address] ~= nil and doclist[self.address][self.name] ~= nil) and doclist[self.address][self.name] or "function" end | |||
} | |||
local vcomponent = {} | |||
function vcomponent.register(address, ctype, proxy, doc) | |||
checkArg(1,address,"string") | |||
checkArg(2,ctype,"string") | |||
checkArg(3,proxy,"table") | |||
if proxylist[address] ~= nil then | |||
return nil, "component already at address" | |||
elseif component.type(address) ~= nil then | |||
return nil, "cannot register over real component" | |||
end | |||
proxy.address = address | |||
proxy.type = ctype | |||
local proxyobj = {} | |||
for k,v in pairs(proxy) do | |||
if type(v) == "function" then | |||
proxyobj[k] = setmetatable({name=k,address=address},componentCallback) | |||
else | |||
proxyobj[k] = v | |||
end | |||
end | |||
proxylist[address] = proxy | |||
proxyobjs[address] = proxyobj | |||
typelist[address] = ctype | |||
doclist[address] = doc | |||
computer.pushSignal("component_added",address,ctype) | |||
return true | |||
end | |||
function vcomponent.unregister(address) | |||
checkArg(1,address,"string") | |||
if proxylist[address] == nil then | |||
if component.type(address) ~= nil then | |||
return nil, "cannot unregister real component" | |||
else | |||
return nil, "no component at address" | |||
end | |||
end | |||
local thetype = typelist[address] | |||
proxylist[address] = nil | |||
proxyobjs[address] = nil | |||
typelist[address] = nil | |||
doclist[address] = nil | |||
computer.pushSignal("component_removed",address,thetype) | |||
return true | |||
end | |||
function vcomponent.list() | |||
local list = {} | |||
for k,v in pairs(proxylist) do | |||
list[#list + 1] = {k,typelist[k],v} | |||
end | |||
return list | |||
end | |||
function vcomponent.resolve(address, componentType) | |||
checkArg(1, address, "string") | |||
checkArg(2, componentType, "string", "nil") | |||
for k,v in pairs(typelist) do | |||
if componentType == nil or v == componentType then | |||
if k:sub(1, #address) == address then | |||
return k | |||
end | |||
end | |||
end | |||
return nil, "no such component" | |||
end | |||
local r = math.random | |||
function vcomponent.uuid() | |||
return string.format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", | |||
r(0,255),r(0,255),r(0,255),r(0,255), | |||
r(0,255),r(0,255), | |||
r(64,79),r(0,255), | |||
r(128,191),r(0,255), | |||
r(0,255),r(0,255),r(0,255),r(0,255),r(0,255),r(0,255)) | |||
end | |||
return vcomponent |
@@ -1,162 +0,0 @@ | |||
local vcomponent = require "vcomponent" | |||
local serial = require "serialization" | |||
local component = require "component" | |||
local computer = require "computer" | |||
local event = require "event" | |||
local imt = require "interminitel" | |||
local cfg = {} | |||
cfg.peers = {} | |||
cfg.rtimer = 5 | |||
cfg.katimer = 30 | |||
local listeners = {} | |||
local proxies = {} | |||
local function loadcfg() | |||
local f = io.open("/boot/cfg/vtunnel.cfg","rb") | |||
if not f then return false end | |||
for k,v in pairs(serial.unserialize(f:read("*a")) or {}) do | |||
cfg[k] = v | |||
end | |||
f:close() | |||
end | |||
local function savecfg() | |||
local f = io.open("/boot/cfg/vtunnel.cfg","wb") | |||
if not f then | |||
print("Warning: unable to save configuration.") | |||
return false | |||
end | |||
f:write(serial.serialize(cfg)) | |||
f:close() | |||
end | |||
local function createTunnel(host,port,addr,raddr) | |||
local proxy = {address=addr,buffer=""} | |||
function proxy.connect() | |||
if proxy.socket then | |||
proxy.socket.close() | |||
end | |||
proxy.socket = component.invoke(component.list("internet")(),"connect",host,port) | |||
local st = computer.uptime() | |||
repeat | |||
coroutine.yield() | |||
until proxy.socket.finishConnect() or computer.uptime() > st+5 | |||
end | |||
function proxy.send(...) | |||
rt = 0 | |||
while not proxy.socket.write(imt.encodePacket(...)) and rt < 10 do | |||
proxy.connect() | |||
rt = rt + 1 | |||
end | |||
proxy.last = computer.uptime() | |||
end | |||
function proxy.read() | |||
local rb, r | |||
local rt = 0 | |||
while true do | |||
rb,r = proxy.socket.read(4096) | |||
if rb or rt > 10 then break end | |||
if type(rb) == "nil" then | |||
proxy.connect() | |||
end | |||
rt = rt + 1 | |||
end | |||
proxy.buffer = proxy.buffer .. rb | |||
while imt.decodePacket(proxy.buffer) do | |||
computer.pushSignal("modem_message",addr,raddr,0,0,imt.decodePacket(proxy.buffer)) | |||
proxy.buffer = imt.getRemainder(proxy.buffer) or "" | |||
end | |||
if computer.uptime() > proxy.last + cfg.katimer then | |||
proxy.socket.write("\0\1\0") | |||
proxy.last = computer.uptime() | |||
end | |||
end | |||
function proxy.getWakeMessage() | |||
return false | |||
end | |||
proxy.setWakeMessage = proxy.getWakeMessage | |||
function proxy.maxPacketSize() | |||
return 8192 | |||
end | |||
function proxy.getChannel() | |||
return host..":"..tostring(port) | |||
end | |||
proxy.connect() | |||
proxy.last = computer.uptime() | |||
return proxy | |||
end | |||
vt = {} | |||
function start() | |||
loadcfg() | |||
for k,v in pairs(cfg.peers) do | |||
print(string.format("Connecting to %s:%d",v.host,v.port)) | |||
v.addr = v.addr or vcomponent.uuid() | |||
v.raddr = v.raddr or vcomponent.uuid() | |||
local px = createTunnel(v.host, v.port, v.addr, v.raddr) | |||
vcomponent.register(v.addr, "tunnel", px) | |||
proxies[v.addr] = px | |||
end | |||
for k,v in pairs(os.tasks()) do | |||
if os.taskInfo(v).name:match("minitel") then | |||
os.kill(v) | |||
end | |||
end | |||
end | |||
function vt.stop() | |||
for k,v in pairs(proxies) do | |||
vcomponent.unregister(k) | |||
end | |||
end | |||
function vt.listpeers() | |||
for k,v in pairs(cfg.peers) do | |||
print(string.format("#%d (%s:%d)\n Local address: %s\n Remote address: %s",k,v.host,v.port,v.addr,v.raddr)) | |||
end | |||
end | |||
function vt.addpeer(host,port) | |||
port = tonumber(port) or 4096 | |||
local t = {} | |||
t.host = host | |||
t.port = port | |||
t.addr = vcomponent.uuid() | |||
t.raddr = vcomponent.uuid() | |||
cfg.peers[#cfg.peers+1] = t | |||
print(string.format("Added peer #%d (%s:%d) to the configuration.\nRestart to apply changes.",#cfg.peers,host,port)) | |||
savecfg() | |||
end | |||
function vt.delpeer(n) | |||
n=tonumber(n) | |||
if not n then | |||
print("delpeer requires a number, representing the peer number, as an argument.") | |||
return false | |||
end | |||
local dp = table.remove(cfg.peers, n) | |||
savecfg() | |||
print(string.format("Removed peer %s:%d",dp.host, dp.port)) | |||
end | |||
function vt.settimer(time) | |||
time = tonumber(time) | |||
if not time then | |||
print("Timer must be a number.") | |||
return false | |||
end | |||
cfg.rtime = time | |||
savecfg() | |||
end | |||
vt.start = start | |||
_G.libs.vtunnel = vt | |||
start() | |||
local last = computer.uptime() | |||
while true do | |||
local tE = {coroutine.yield()} | |||
if computer.uptime() > last + cfg.rtimer then | |||
for k,v in pairs(proxies) do | |||
v.read() | |||
end | |||
last = computer.uptime() | |||
end | |||
end |