2017-03-20 11:40:28 +11:00
|
|
|
-- I, 20kdc, release this into the public domain.
|
|
|
|
-- No warranty is provided, implied or otherwise.
|
|
|
|
|
|
|
|
-- Copper Hierarchial Gateway implementation for OpenComputers.
|
2017-03-23 10:05:31 +11:00
|
|
|
-- 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
|
2017-03-20 11:40:28 +11:00
|
|
|
-- they're inconvenient to use and cdlib needs to be there -
|
|
|
|
-- I'm sure you can port it yourself.
|
|
|
|
|
|
|
|
local args = {...}
|
|
|
|
|
2017-03-23 10:05:31 +11:00
|
|
|
if #args ~= 2 then
|
|
|
|
error("Expecting args: outboundModem (or 'relay' to use Linked Cards), network-name")
|
2017-03-20 11:40:28 +11:00
|
|
|
end
|
|
|
|
|
|
|
|
-- What is the name of this division, including forward-slash?
|
|
|
|
local netname = args[2] .. "/"
|
|
|
|
|
2017-03-23 10:05:31 +11:00
|
|
|
local outboundModemAdr = args[1]
|
|
|
|
|
|
|
|
-- These names are chosen by sending direction.
|
|
|
|
local outboundModem, inboundModem
|
|
|
|
|
|
|
|
if outboundModemAdr == "relay" then outboundModemAdr = nil end
|
|
|
|
|
2017-03-20 11:40:28 +11:00
|
|
|
local event = require("event")
|
|
|
|
local component = require("component")
|
|
|
|
local cdlib = require("cdlib")
|
|
|
|
|
2017-03-23 10:05:31 +11:00
|
|
|
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
|
2017-03-20 11:40:28 +11:00
|
|
|
end
|
2017-03-23 10:05:31 +11:00
|
|
|
|
|
|
|
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.)
|
2017-03-20 11:40:28 +11:00
|
|
|
|
|
|
|
-- 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.
|
2017-03-23 10:05:31 +11:00
|
|
|
-- "incoming" is parent-side, incoming being false means child-side.
|
|
|
|
processFrom = function (incoming, from)
|
|
|
|
if incoming then
|
2017-03-20 11:40:28 +11:00
|
|
|
if from:sub(1, netname:len()) == netname then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
return "<" .. from
|
2017-03-23 10:05:31 +11:00
|
|
|
else
|
2017-03-20 11:40:28 +11:00
|
|
|
if from:sub(1, 1) == "<" then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
return netname .. from
|
|
|
|
end
|
2017-03-23 10:05:31 +11:00
|
|
|
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
|
2017-03-21 23:50:23 +11:00
|
|
|
if nto:sub(1, 1) ~= "<" then
|
2017-03-20 11:40:28 +11:00
|
|
|
return
|
|
|
|
end
|
2017-03-21 23:50:23 +11:00
|
|
|
return nto:sub(2)
|
2017-03-20 11:40:28 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function checkLen(s)
|
2017-03-21 23:50:23 +11:00
|
|
|
if not s then return end
|
2017-03-20 11:40:28 +11:00
|
|
|
if s:len() == 0 then return end
|
|
|
|
if s:len() > 256 then return end
|
|
|
|
return s
|
|
|
|
end
|
|
|
|
|
2017-03-23 10:05:31 +11:00
|
|
|
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
|
|
|
|
|
2017-03-20 11:40:28 +11:00
|
|
|
while true do
|
|
|
|
local e = {event.pull("modem_message")}
|
|
|
|
if e[1] == "modem_message" then
|
2017-03-23 10:05:31 +11:00
|
|
|
-- 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])
|
2017-03-20 11:40:28 +11:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|