mirror of
https://github.com/XeonSquared/OC-Copper.git
synced 2024-11-27 03:38:05 +11:00
OC Internet Card transport and a LuaSocket server for it.
This commit is contained in:
parent
d18c935db2
commit
f6387530a4
78
oc/tcpdial.lua
Normal file
78
oc/tcpdial.lua
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
-- I, 20kdc, release this into the public domain.
|
||||||
|
-- No warranty is provided, implied or otherwise.
|
||||||
|
|
||||||
|
-- Light Copper Base node that communicates to a server via an
|
||||||
|
-- Internet Card.
|
||||||
|
-- Only handles one modem for now.
|
||||||
|
|
||||||
|
local args = {...}
|
||||||
|
if #args ~= 3 then error("name, tcphost, tcpport") end
|
||||||
|
|
||||||
|
local component = require("component")
|
||||||
|
local cdlib = require("cdlib")
|
||||||
|
local event = require("event")
|
||||||
|
|
||||||
|
-- Adjust to taste.
|
||||||
|
local tcp = require("internet").open(tostring(args[2]), tonumber(args[3]))
|
||||||
|
|
||||||
|
local md = component.modem
|
||||||
|
md.open(4957)
|
||||||
|
|
||||||
|
tcp:setTimeout(0.05)
|
||||||
|
tcp:write(string.char((#(args[1])) - 1) .. args[1])
|
||||||
|
tcp:flush()
|
||||||
|
|
||||||
|
print("TCP up")
|
||||||
|
|
||||||
|
local function verify(d)
|
||||||
|
local hops, src, dst, data = cdlib.decode(d)
|
||||||
|
if not data then return end
|
||||||
|
-- Just a bit of filtering
|
||||||
|
if dst:sub(1, 1) ~= "<" then return end
|
||||||
|
if d:len() > 2021 then return end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readByte()
|
||||||
|
while true do
|
||||||
|
local ok, err = pcall(tcp.read, tcp, 1)
|
||||||
|
if ok then return err end
|
||||||
|
-- not nice :(
|
||||||
|
if err:find("timeout") then
|
||||||
|
coroutine.yield()
|
||||||
|
else
|
||||||
|
error(err)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function readerRoutine()
|
||||||
|
while true do
|
||||||
|
local h = readByte()
|
||||||
|
local l = readByte()
|
||||||
|
local sz = string.byte(l) + (string.byte(h) * 256)
|
||||||
|
print("Incoming packet size " .. sz)
|
||||||
|
local dat = ""
|
||||||
|
for i = 1, sz do
|
||||||
|
dat = dat .. readByte()
|
||||||
|
end
|
||||||
|
md.broadcast(4957, "copper", dat)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local primary = coroutine.create(readerRoutine)
|
||||||
|
while true do
|
||||||
|
local et, _, _, p, _, m, d = event.pull(0.1)
|
||||||
|
local ok, o = coroutine.resume(primary)
|
||||||
|
if not ok then error(o) end
|
||||||
|
if et == "modem_message" and p == 4957 then
|
||||||
|
if m == "copper" then
|
||||||
|
-- Incoming Copper packet.
|
||||||
|
if verify(d) then
|
||||||
|
local h = math.floor(d:len() / 256)
|
||||||
|
local l = d:len() % 256
|
||||||
|
tcp:write(string.char(h, l) .. d)
|
||||||
|
tcp:flush()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
129
tcprouter.lua
Normal file
129
tcprouter.lua
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
-- TCP-based 'absolute root' node
|
||||||
|
-- Used to connect servers together.
|
||||||
|
-- A smart routing node which includes hierarchial gateways.
|
||||||
|
-- (Note: Multiple "absolute root" nodes can be used at once,
|
||||||
|
-- and things should work as long as there are no conflicts and
|
||||||
|
-- everybody who wants to communicate has a common root node.
|
||||||
|
-- A less efficient but potentially better approach would be to just
|
||||||
|
-- have TCP-based Copper meshnet nodes
|
||||||
|
-- and run the gateways on the MC servers.)
|
||||||
|
|
||||||
|
local socket = require("socket")
|
||||||
|
local t = socket.bind("*", 4957)
|
||||||
|
|
||||||
|
local cdlib = require("cdlib")
|
||||||
|
|
||||||
|
local sockets = {}
|
||||||
|
|
||||||
|
local function getbyte(s)
|
||||||
|
s:settimeout(0.05)
|
||||||
|
while true do
|
||||||
|
local d, e = s:receive(1)
|
||||||
|
if not d then
|
||||||
|
if e ~= "timeout" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
coroutine.yield()
|
||||||
|
else
|
||||||
|
return d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local function getpacket(s)
|
||||||
|
local h = getbyte(s)
|
||||||
|
if not h then error("connection failed") end
|
||||||
|
local l = getbyte(s)
|
||||||
|
if not l then error("framing bad") end
|
||||||
|
local sz = string.byte(l) + (string.byte(h) * 256)
|
||||||
|
if sz > 2021 then error("packet too large") end
|
||||||
|
local data = ""
|
||||||
|
for i = 1, sz do
|
||||||
|
local dbt = getbyte(s)
|
||||||
|
if not dbt then error("terminated early") end
|
||||||
|
data = data .. dbt
|
||||||
|
end
|
||||||
|
return cdlib.decode(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkLen(name)
|
||||||
|
if name:len() == 0 then return end
|
||||||
|
if name:len() > 256 then return end
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
local function translateSend(hops, src, dst, data, srname, tgsock, tgname)
|
||||||
|
if src:sub(1, 1) == "<" then return end
|
||||||
|
if dst:sub(1, tgname:len() + 2) ~= "<" .. tgname .. "/" then return end
|
||||||
|
-- Ok, all rejection rules have been handled
|
||||||
|
src = "<" .. srname .. "/" .. src
|
||||||
|
dst = dst:sub(tgname:len() + 3)
|
||||||
|
src, dst = checkLen(src), checkLen(dst)
|
||||||
|
if src and dst then
|
||||||
|
local enc = cdlib.encode(hops, src, dst, data)
|
||||||
|
local h = math.floor(enc:len() / 256)
|
||||||
|
local l = enc:len() % 256
|
||||||
|
local frame = string.char(h, l)
|
||||||
|
tgsock:send(frame .. enc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The main coroutine.
|
||||||
|
-- Moves messages around the system.
|
||||||
|
local function messageroutine(tbl)
|
||||||
|
local b = getbyte(tbl[2])
|
||||||
|
if not b then error("Didn't even send name") end
|
||||||
|
local name = ""
|
||||||
|
-- 0 is not a typo, this follows Copper name format.
|
||||||
|
for i = 0, string.byte(b) do
|
||||||
|
local b2 = getbyte(tbl[2])
|
||||||
|
if not b2 then error("Didn't even complete name") end
|
||||||
|
name = name .. b2
|
||||||
|
end
|
||||||
|
print("confirmed name " .. name)
|
||||||
|
tbl[3] = name
|
||||||
|
while true do
|
||||||
|
local hops, src, dst, data = getpacket(tbl[2])
|
||||||
|
if not data then
|
||||||
|
error("Bad Copper packet")
|
||||||
|
end
|
||||||
|
print("packet", src, dst)
|
||||||
|
if hops ~= 255 then
|
||||||
|
for _, v in ipairs(sockets) do
|
||||||
|
if v[3] then
|
||||||
|
if v ~= tbl then
|
||||||
|
translateSend(hops + 1, src, dst, data, name, v[2], v[3])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
t:settimeout(0.05)
|
||||||
|
local ns = t:accept()
|
||||||
|
if ns then
|
||||||
|
-- Note: packets are ~500 bytes, Copper data can be ~2KB
|
||||||
|
-- but it isn't USUALLY that way.
|
||||||
|
-- ns:setoption("tcp-nodelay", true)
|
||||||
|
ns:setoption("keepalive", true)
|
||||||
|
print("incoming")
|
||||||
|
local co = coroutine.create(messageroutine)
|
||||||
|
local tbl = {co, ns}
|
||||||
|
table.insert(sockets, tbl)
|
||||||
|
local ok, r = coroutine.resume(co, tbl)
|
||||||
|
if not ok then
|
||||||
|
-- do cleanup later
|
||||||
|
print("connection died quick", r)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local i = 1
|
||||||
|
while i <= #sockets do
|
||||||
|
local ok, r = coroutine.resume(sockets[i][1])
|
||||||
|
if not ok then
|
||||||
|
print("dropping conn", r)
|
||||||
|
table.remove(sockets, i)[2]:close()
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user