OC-Copper/oc/hierarchi.lua

133 lines
3.6 KiB
Lua

-- I, 20kdc, release this into the public domain.
-- No warranty is provided, implied or otherwise.
-- Copper Hierarchial Gateway implementation for OpenComputers.
-- Best run in a Server with two modems,
-- but can be run with a support program on another computer with a modem,
-- one modem in the main computer, and a LC pair.
-- The only reason this isn't run on two microcontrollers is because
-- they're inconvenient to use and cdlib needs to be there -
-- I'm sure you can port it yourself.
local args = {...}
if #args ~= 2 then
error("Expecting args: outboundModem (or 'relay' to use Linked Cards), network-name")
end
-- What is the name of this division, including forward-slash?
local netname = args[2] .. "/"
local outboundModemAdr = args[1]
-- These names are chosen by sending direction.
local outboundModem, inboundModem
if outboundModemAdr == "relay" then outboundModemAdr = nil end
local event = require("event")
local component = require("component")
local cdlib = require("cdlib")
for a, _ in component.list("modem") do
if outboundModemAdr and (a:sub(1, outboundModemAdr:len()) == outboundModemAdr) then
if outboundModem then error("Outbound modem ambiguous.") end
outboundModem = component.proxy(a)
else
if inboundModem then error("More than one internal-side modem.") end
inboundModem = component.proxy(a)
end
end
inboundModem.open(4957)
if not outboundModem then
local tunnel = component.tunnel
-- Implement just enough of an outbound modem to be useful.
outboundModem = {
address = tunnel.address,
broadcast = function (port, ...)
tunnel.send(...)
end
}
else
outboundModem.open(4957)
end
------ By this point, inboundModem and outboundModem must be:
-- 1. non-nil
-- 2. have the address and broadcast(port, ...) fields
-- (Also, if outboundModemAdr == nil then the port will be ignored for it.)
-- Rules used on messages coming in from the 'modem' side.
-- (This implies Tunnel packets are trusted absolutely - which is correct.)
local processFrom, processTo
-- Implementation of the rules described in protocol.1 for more or
-- less unambiguous name translation.
-- "incoming" is parent-side, incoming being false means child-side.
processFrom = function (incoming, from)
if incoming then
if from:sub(1, netname:len()) == netname then
return
end
return "<" .. from
else
if from:sub(1, 1) == "<" then
return
end
return netname .. from
end
end
processTo = function (incoming, nto)
if incoming then
if nto:sub(1, netname:len()) ~= netname then
return
end
return nto:sub(netname:len() + 1)
else
if nto:sub(1, 1) ~= "<" then
return
end
return nto:sub(2)
end
end
local function checkLen(s)
if not s then return end
if s:len() == 0 then return end
if s:len() > 256 then return end
return s
end
local function handlePacket(incoming, dat)
local hops, nfrom, nto, data = cdlib.decode(dat)
if not data then return end -- corrupt packet
if hops == 255 then return end
local tfrom, tto = checkLen(processFrom(nfrom)), checkLen(processTo(nto))
if tfrom and tto then
local resdata = cdlib.encode(hops + 1, tfrom, tto, data)
if incoming then
inboundModem.broadcast(4957, "copper", resdata)
else
outboundModem.broadcast(4957, "copper", resdata)
end
end
end
while true do
local e = {event.pull("modem_message")}
if e[1] == "modem_message" then
-- type, receiver, sender, port, dist, magic, data
if (e[2] == inboundModem.address) or (e[2] == outboundModem.address) then
local incoming = e[2] == outboundModem.address
if (e[4] == 4957) or (incoming and (outboundModemAdr == nil)) then
if (e[6] == "copper") and e[7] then
handlePacket(incoming, e[7])
end
end
end
end
end