From 4ef792a06d60c4a8d63d8a04ae4d86269488b478 Mon Sep 17 00:00:00 2001 From: XeonSquared Date: Fri, 20 Dec 2019 17:48:47 +1100 Subject: [PATCH] added an initial vtunnel implementation --- lib/interminitel.lua | 63 +++++++++++++++ service/vtunnel.lua | 177 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 lib/interminitel.lua create mode 100644 service/vtunnel.lua diff --git a/lib/interminitel.lua b/lib/interminitel.lua new file mode 100644 index 0000000..7cc4f04 --- /dev/null +++ b/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/service/vtunnel.lua b/service/vtunnel.lua new file mode 100644 index 0000000..dda0f98 --- /dev/null +++ b/service/vtunnel.lua @@ -0,0 +1,177 @@ +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 timers = {} +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, read +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,tr = createTunnel(v.host, v.port, v.addr, v.raddr) + vcomponent.register(v.addr, "tunnel", px) + timers[v.addr] = tr + 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(listeners) do + event.ignore(v[1],v[2]) + end + for k,v in pairs(timers) do + event.cancel(v) + end + 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 + +print(pcall(start)) +local last = computer.uptime() +while true do + local tE = {coroutine.yield()} + if computer.uptime() > last + cfg.rtimer then + for k,v in pairs(timers) do + print(pcall(v)) + end + last = computer.uptime() + end +end + +--[[ +_G.vtunnel = {} + +_G.vtunnel.start = start +_G.vtunnel.delpeer = delpeer +]]