potatOS/src/bin/relay.lua

87 lines
3.0 KiB
Lua

--[[
For reasons outlined here (https://wiki.computercraft.cc/Network_security), Rednet is not really a good choice for new networking setups, apart from its capability to relay messages (obsoleted by ender modems).
However, if you do want to keep using it, without the risk of it crashing due to the exploits I have identified (https://pastebin.com/pJnfSDcL), you can use this convenient, patched repeater. It doesn't alleviate the fundamental issues with Rednet, though.
]]
-- Find modems
local modems = {peripheral.find "modem"}
local comp_ID = os.getComputerID()
print(("%d modem(s) found"):format(#modems))
local function for_each_modem(fn)
for _, m in pairs(modems) do
fn(m)
end
end
local function open(channel)
for_each_modem(function(m) m.open(channel) end)
end
local function close(channel)
for_each_modem(function(m) m.close(channel) end)
end
-- Open channels
open(rednet.CHANNEL_REPEAT)
-- Main loop (terminate to break)
local ok, error = pcall(function()
local received_messages = {}
local received_message_timers = {}
local transmitted_messages = 0
while true do
local event, modem, channel, reply_channel, message, distance = os.pullEvent()
if event == "modem_message" then
-- Got a modem message, rebroadcast it if it's a rednet thing
if channel == rednet.CHANNEL_REPEAT and type(message) == "table" then
local id = message.nMessageID -- unfortunately we must keep the stupid rednet identifiers SOMEWHERE...
local recipient = message.nRecipient
local route = message.route -- protocol extension
if type(route) ~= "table" then route = { reply_channel } end
table.insert(route, comp_ID)
message.route = route
if id and recipient and (type(id) == "number" or type(id) == "string") and type(recipient) == "number" and recipient >= 0 and recipient <= 65535 then
if not received_messages[id] then
-- Ensure we only repeat a message once per 30 seconds
received_messages[id] = true
received_message_timers[os.startTimer(30)] = id
-- Send on all other open modems, to the target and to other repeaters
for_each_modem(function(m)
m.transmit(rednet.CHANNEL_REPEAT, reply_channel, message)
m.transmit(recipient, reply_channel, message)
end)
-- Log the event
transmitted_messages = transmitted_messages + 1
term.clear()
term.setCursorPos(1, 1)
print(("%d message(s) repeated"):format(transmitted_messages))
print(string.format("%s\nfrom %d to %s dist %d", tostring(message.message), reply_channel, tostring(recipient), tostring(distance) or "[n/a]"))
end
end
end
elseif event == "timer" then
-- Got a timer event, use it to clear the message history
local timer = modem
local id = received_message_timers[timer]
if id then
received_message_timers[timer] = nil
received_messages[timer] = nil
end
end
end
end)
if not ok then
printError(error)
end
-- Close channels
close(rednet.CHANNEL_REPEAT)