From 104e59e6be0c1f6c87e8b25fea9a145539312686 Mon Sep 17 00:00:00 2001 From: XeonSquared Date: Sun, 7 Jun 2020 10:33:36 +1000 Subject: [PATCH] import some packages from the main system --- vcomponent/lib/vcomponent.lua | 197 ++++++++++++++++++++++++++++++++++ vcomponent/package.cfg | 3 + vtunnel/lib/interminitel.lua | 63 +++++++++++ vtunnel/package.cfg | 4 + vtunnel/service/vtunnel.lua | 162 ++++++++++++++++++++++++++++ 5 files changed, 429 insertions(+) create mode 100644 vcomponent/lib/vcomponent.lua create mode 100644 vcomponent/package.cfg create mode 100644 vtunnel/lib/interminitel.lua create mode 100644 vtunnel/package.cfg create mode 100644 vtunnel/service/vtunnel.lua diff --git a/vcomponent/lib/vcomponent.lua b/vcomponent/lib/vcomponent.lua new file mode 100644 index 0000000..4dbbe1c --- /dev/null +++ b/vcomponent/lib/vcomponent.lua @@ -0,0 +1,197 @@ +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 diff --git a/vcomponent/package.cfg b/vcomponent/package.cfg new file mode 100644 index 0000000..6ec4fe1 --- /dev/null +++ b/vcomponent/package.cfg @@ -0,0 +1,3 @@ +{["name"]="vComponent", + ["description"]="Library for creating virtual component devices.", + ["authors"]="gamax92"} diff --git a/vtunnel/lib/interminitel.lua b/vtunnel/lib/interminitel.lua new file mode 100644 index 0000000..7cc4f04 --- /dev/null +++ b/vtunnel/lib/interminitel.lua @@ -0,0 +1,63 @@ +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 diff --git a/vtunnel/package.cfg b/vtunnel/package.cfg new file mode 100644 index 0000000..dc32e5b --- /dev/null +++ b/vtunnel/package.cfg @@ -0,0 +1,4 @@ +{["name"]="vTunnel", + ["description"]="Virtual tunnel card over TCP", + ["authors"]="Izaya", + ["dependencies"]={"vcomponent"}} diff --git a/vtunnel/service/vtunnel.lua b/vtunnel/service/vtunnel.lua new file mode 100644 index 0000000..f34c0ef --- /dev/null +++ b/vtunnel/service/vtunnel.lua @@ -0,0 +1,162 @@ +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