Browse Source

added a minitel stack, using the same code as the OpenOS version

master
XeonSquared 5 months ago
parent
commit
50c7a3841c
3 changed files with 507 additions and 0 deletions
  1. 42
    0
      lib/event.lua
  2. 167
    0
      lib/minitel.lua
  3. 298
    0
      service/minitel.lua

+ 42
- 0
lib/event.lua View File

@@ -0,0 +1,42 @@
1
+local event = {}
2
+function event.pull(t,...)
3
+ local tA = {...}
4
+ if type(t) == "string" then
5
+  table.insert(tA,1,t)
6
+  t = 0
7
+ end
8
+ if not t or t <= 0 then
9
+  t = math.huge
10
+ end
11
+ local tE = computer.uptime()+t
12
+ repeat
13
+  tEv = {coroutine.yield()}
14
+  local ret = true
15
+  for i = 1, #tA do
16
+   if not (tEv[i] or ""):match(tA[i]) then
17
+    ret = false
18
+   end
19
+  end
20
+  if ret then return table.unpack(tEv) end
21
+ until computer.uptime() > tE
22
+ return nil
23
+end
24
+
25
+function event.listen(e,f)
26
+ local op = os.getenv("parent")
27
+ os.setenv("parent",cPid)
28
+ os.spawn(function() while true do
29
+  local tEv = {coroutine.yield()}
30
+  if tEv[1] == e then
31
+   f(table.unpack(tEv))
32
+  end
33
+  if not tTasks[os.getenv("parent")] or (tEv[1] == "unlisten" and tEv[2] == e and tEv[3] == tostring(f)) then break end
34
+ end end,string.format("[%d] %s listener",cPid,e))
35
+ os.setenv("parent",op)
36
+end
37
+
38
+function event.ignore(e,f)
39
+ computer.pushSignal("unlisten",e,tostring(f))
40
+end
41
+
42
+return event

+ 167
- 0
lib/minitel.lua View File

@@ -0,0 +1,167 @@
1
+local computer,event = computer,event
2
+if _OSVERSION:sub(1,6) == "OpenOS" then
3
+ computer = require "computer"
4
+ event = require "event"
5
+elseif _OSVERSION:sub(1,7) == "PsychOS" then
6
+ event = require "event"
7
+end
8
+local net = {}
9
+net.mtu = 4096
10
+net.streamdelay = 30
11
+net.minport = 32768
12
+net.maxport = 65535
13
+net.openports = {}
14
+
15
+function net.genPacketID()
16
+ local npID = ""
17
+ for i = 1, 16 do
18
+  npID = npID .. string.char(math.random(32,126))
19
+ end
20
+ return npID
21
+end
22
+
23
+function net.usend(to,port,data,npID)
24
+ computer.pushSignal("net_send",0,to,port,data,npID)
25
+end
26
+
27
+function net.rsend(to,port,data,block)
28
+ local pid, stime = net.genPacketID(), computer.uptime() + net.streamdelay
29
+ computer.pushSignal("net_send",1,to,port,data,pid)
30
+ if block then return false end
31
+ repeat
32
+  _,rpid = event.pull(0.5,"net_ack")
33
+ until rpid == pid or computer.uptime() > stime
34
+ if not rpid then return false end
35
+ return true
36
+end
37
+
38
+-- ordered packet delivery, layer 4?
39
+
40
+function net.send(to,port,ldata)
41
+ local tdata = {}
42
+ if ldata:len() > net.mtu then
43
+  for i = 1, ldata:len(), net.mtu do
44
+   tdata[#tdata+1] = ldata:sub(1,net.mtu)
45
+   ldata = ldata:sub(net.mtu+1)
46
+  end
47
+ else
48
+  tdata = {ldata}
49
+ end
50
+ for k,v in ipairs(tdata) do
51
+  if not net.rsend(to,port,v) then return false end
52
+ end
53
+ return true
54
+end
55
+
56
+-- socket stuff, layer 5?
57
+
58
+local function cwrite(self,data)
59
+ if self.state == "open" then
60
+  if not net.send(self.addr,self.port,data) then
61
+   self:close()
62
+   return false, "timed out"
63
+  end
64
+ end
65
+end
66
+local function cread(self,length)
67
+ length = length or "\n"
68
+ local rdata = ""
69
+ if type(length) == "number" then
70
+  rdata = self.rbuffer:sub(1,length)
71
+  self.rbuffer = self.rbuffer:sub(length+1)
72
+  return rdata
73
+ elseif type(length) == "string" then
74
+  if length:sub(1,2) == "*a" then
75
+   rdata = self.rbuffer
76
+   self.rbuffer = ""
77
+   return rdata
78
+  elseif length:len() == 1 then
79
+   local pre, post = self.rbuffer:match("(.-)"..length.."(.*)")
80
+   if pre and post then
81
+    self.rbuffer = post
82
+    return pre
83
+   end
84
+   return nil
85
+  end
86
+ end
87
+end
88
+
89
+local function socket(addr,port,sclose)
90
+ local conn = {}
91
+ conn.addr,conn.port = addr,tonumber(port)
92
+ conn.rbuffer = ""
93
+ conn.write = cwrite
94
+ conn.read = cread
95
+ conn.state = "open"
96
+ conn.sclose = sclose
97
+ local function listener(_,f,p,d)
98
+  if f == conn.addr and p == conn.port then
99
+   if d == sclose then
100
+    conn:close()
101
+   else
102
+    conn.rbuffer = conn.rbuffer .. d
103
+   end
104
+  end
105
+ end
106
+ event.listen("net_msg",listener)
107
+ function conn.close(self)
108
+  event.ignore("net_msg",listener)
109
+  conn.state = "closed"
110
+  net.rsend(addr,port,sclose)
111
+ end
112
+ return conn
113
+end
114
+
115
+function net.open(to,port)
116
+ if not net.rsend(to,port,"openstream") then return false, "no ack from host" end
117
+ local st = computer.uptime()+net.streamdelay
118
+ local est = false
119
+ while true do
120
+  _,from,rport,data = event.pull("net_msg")
121
+  if to == from and rport == port then
122
+   if tonumber(data) then
123
+    est = true
124
+   end
125
+   break
126
+  end
127
+  if st < computer.uptime() then
128
+   return nil, "timed out"
129
+  end
130
+ end
131
+ if not est then
132
+  return nil, "refused"
133
+ end
134
+ data = tonumber(data)
135
+ sclose = ""
136
+ repeat
137
+  _,from,nport,sclose = event.pull("net_msg")
138
+ until from == to and nport == data
139
+ return socket(to,data,sclose)
140
+end
141
+
142
+function net.listen(port)
143
+ repeat
144
+  _, from, rport, data = event.pull("net_msg")
145
+ until rport == port and data == "openstream"
146
+ local nport = math.random(net.minport,net.maxport)
147
+ local sclose = net.genPacketID()
148
+ net.rsend(from,rport,tostring(nport))
149
+ net.rsend(from,nport,sclose)
150
+ return socket(from,nport,sclose)
151
+end
152
+
153
+function net.flisten(port,listener)
154
+ local function helper(_,from,rport,data)
155
+  if rport == port and data == "openstream" then
156
+   local nport = math.random(net.minport,net.maxport)
157
+   local sclose = net.genPacketID()
158
+   net.rsend(from,rport,tostring(nport))
159
+   net.rsend(from,nport,sclose)
160
+   listener(socket(from,nport,sclose))
161
+  end
162
+ end
163
+ event.listen("net_msg",helper)
164
+ return helper
165
+end
166
+
167
+return net

+ 298
- 0
service/minitel.lua View File

@@ -0,0 +1,298 @@
1
+--[[
2
+packet format:
3
+packetID: random string to differentiate
4
+packetType:
5
+ - 0: unreliable
6
+ - 1: reliable, requires ack
7
+ - 2: ack packet
8
+destination: end destination hostname
9
+sender: original sender of packet
10
+data: the actual packet data, duh.
11
+]]--
12
+
13
+local listeners = {}
14
+local modems = {}
15
+
16
+local cfg = {}
17
+cfg.debug = false
18
+cfg.port = 4096
19
+cfg.retry = 10
20
+cfg.retrycount = 64
21
+cfg.route = true
22
+
23
+local hostname = computer.address():sub(1,8)
24
+
25
+-- packet cache: [packet ID]=uptime
26
+local pcache = {}
27
+cfg.pctime = 30
28
+
29
+--[[
30
+LKR format:
31
+address {
32
+ local hardware address
33
+ remote hardware address
34
+ time last received
35
+}
36
+]]--
37
+
38
+cfg.sroutes = {}
39
+local rcache = setmetatable({},{__index=cfg.sroutes})
40
+cfg.rctime = 15
41
+
42
+--[[
43
+packet queue format:
44
+{
45
+ packetID,
46
+ packetType
47
+ destination,
48
+ data,
49
+ timestamp,
50
+ attempts
51
+}
52
+]]--
53
+local pqueue = {}
54
+
55
+local function loadconfig()
56
+end
57
+local function saveconfig()
58
+end
59
+
60
+-- specific OS support here
61
+if _OSVERSION:sub(1,6) == "OpenOS" then -- OpenOS specific code
62
+ local timers = {}
63
+
64
+ local event = require "event"
65
+ local component = require "component"
66
+ local computer = require "computer"
67
+ local serial = require "serialization"
68
+ local listener = false
69
+
70
+ local function saveconfig()
71
+  local f = io.open("/etc/minitel.cfg","wb")
72
+  if f then
73
+   f:write(serial.serialize(cfg))
74
+   f:close()
75
+  end
76
+ end
77
+ local function loadconfig()
78
+  local f=io.open("/etc/hostname","rb")
79
+  if f then
80
+   hostname = f:read()
81
+   f:close()
82
+  end
83
+  local f = io.open("/etc/minitel.cfg","rb")
84
+  if f then
85
+   local newcfg = serial.unserialize(f:read("*a"))
86
+   f:close()
87
+   for k,v in pairs(newcfg) do
88
+    cfg[k] = v
89
+   end
90
+  else
91
+   saveconfig()
92
+  end
93
+ end
94
+ function stop()
95
+  for k,v in pairs(listeners) do
96
+   event.ignore(k,v)
97
+   print("Stopped listener: "..tostring(v))
98
+  end
99
+  for k,v in pairs(timers) do
100
+   event.cancel(v)
101
+   print("Stopped timer: "..tostring(v))
102
+  end
103
+ end
104
+ 
105
+ function set(k,v)
106
+  if type(cfg[k]) == "string" then
107
+   cfg[k] = v
108
+  elseif type(cfg[k]) == "number" then
109
+   cfg[k] = tonumber(v)
110
+  elseif type(cfg[k]) == "boolean" then
111
+   if v:lower():sub(1,1) == "t" then
112
+    cfg[k] = true
113
+   else
114
+    cfg[k] = false
115
+   end
116
+  end
117
+  print("cfg."..k.." = "..tostring(cfg[k]))
118
+  saveconfig()
119
+ end
120
+ 
121
+ function set_route(to,laddr,raddr)
122
+  cfg.sroutes[to] = {laddr,raddr,0}
123
+ end
124
+ function del_route(to)
125
+  cfg.sroutes[to] = nil
126
+ end
127
+end
128
+
129
+local function dprint(...)
130
+ if cfg.debug then
131
+  print(...)
132
+ end
133
+end
134
+
135
+
136
+function start()
137
+ loadconfig()
138
+ print("Hostname: "..hostname)
139
+ if listener then return end
140
+ modems={}
141
+ for a,t in component.list("modem") do
142
+  modems[#modems+1] = component.proxy(a)
143
+ end
144
+ for k,v in ipairs(modems) do
145
+  v.open(cfg.port)
146
+  print("Opened port "..cfg.port.." on "..v.address:sub(1,8))
147
+ end
148
+ for a,t in component.list("tunnel") do
149
+  modems[#modems+1] = component.proxy(a)
150
+ end
151
+ 
152
+ local function genPacketID()
153
+  local npID = ""
154
+  for i = 1, 16 do
155
+   npID = npID .. string.char(math.random(32,126))
156
+  end
157
+  return npID
158
+ end
159
+ 
160
+ local function sendPacket(packetID,packetType,dest,sender,vPort,data)
161
+  if rcache[dest] then
162
+   dprint("Cached", rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
163
+   if component.type(rcache[dest][1]) == "modem" then
164
+    component.invoke(rcache[dest][1],"send",rcache[dest][2],cfg.port,packetID,packetType,dest,sender,vPort,data)
165
+   elseif component.type(rcache[dest][1]) == "tunnel" then
166
+    component.invoke(rcache[dest][1],"send",packetID,packetType,dest,sender,vPort,data)
167
+   end
168
+  else
169
+   dprint("Not cached", cfg.port,packetID,packetType,dest,sender,vPort,data)
170
+   for k,v in pairs(modems) do
171
+    if v.type == "modem" then
172
+     v.broadcast(cfg.port,packetID,packetType,dest,sender,vPort,data)
173
+    elseif v.type == "tunnel" then
174
+     v.send(packetID,packetType,dest,sender,vPort,data)
175
+    end
176
+   end
177
+  end
178
+ end
179
+ 
180
+ local function pruneCache()
181
+  for k,v in pairs(rcache) do
182
+   dprint(k,v[3],computer.uptime())
183
+   if v[3] < computer.uptime() then
184
+    rcache[k] = nil
185
+    dprint("pruned "..k.." from routing cache")
186
+   end
187
+  end
188
+  for k,v in pairs(pcache) do
189
+   if v < computer.uptime() then
190
+    pcache[k] = nil
191
+    dprint("pruned "..k.." from packet cache")
192
+   end
193
+  end
194
+ end
195
+
196
+ local function checkPCache(packetID)
197
+  dprint(packetID)
198
+  for k,v in pairs(pcache) do
199
+   dprint(k)
200
+   if k == packetID then return true end
201
+  end
202
+  return false
203
+ end
204
+ 
205
+ local function processPacket(_,localModem,from,pport,_,packetID,packetType,dest,sender,vPort,data)
206
+  pruneCache()
207
+  if pport == cfg.port or pport == 0 then -- for linked cards
208
+   dprint(cfg.port,vPort,packetType,dest)
209
+   if checkPCache(packetID) then return end
210
+   if dest == hostname then
211
+    if packetType == 1 then
212
+     sendPacket(genPacketID(),2,sender,hostname,vPort,packetID)
213
+    end
214
+    if packetType == 2 then
215
+     dprint("Dropping "..data.." from queue")
216
+     pqueue[data] = nil
217
+     computer.pushSignal("net_ack",data)
218
+    end
219
+    if packetType ~= 2 then
220
+     computer.pushSignal("net_msg",sender,vPort,data)
221
+    end
222
+   elseif dest:sub(1,1) == "~" then -- broadcasts start with ~
223
+    computer.pushSignal("net_broadcast",sender,vPort,data)
224
+   elseif cfg.route then -- repeat packets if route is enabled
225
+    sendPacket(packetID,packetType,dest,sender,vPort,data)
226
+   end
227
+   if not rcache[sender] then -- add the sender to the rcache
228
+    dprint("rcache: "..sender..":", localModem,from,computer.uptime())
229
+    rcache[sender] = {localModem,from,computer.uptime()+cfg.rctime}
230
+   end
231
+   if not pcache[packetID] then -- add the packet ID to the pcache
232
+    pcache[packetID] = computer.uptime()+cfg.pctime
233
+   end
234
+  end
235
+ end
236
+ 
237
+ local function queuePacket(_,ptype,to,vPort,data,npID)
238
+  npID = npID or genPacketID()
239
+  if to == hostname or to == "localhost" then
240
+   computer.pushSignal("net_msg",to,vPort,data)
241
+   computer.pushSignal("net_ack",npID)
242
+   return
243
+  end
244
+  pqueue[npID] = {ptype,to,vPort,data,0,0}
245
+  dprint(npID,table.unpack(pqueue[npID]))
246
+ end
247
+ 
248
+ 
249
+ local function packetPusher()
250
+  for k,v in pairs(pqueue) do
251
+   if v[5] < computer.uptime() then
252
+    dprint(k,v[1],v[2],hostname,v[3],v[4])
253
+    sendPacket(k,v[1],v[2],hostname,v[3],v[4])
254
+    if v[1] ~= 1 or v[6] == cfg.retrycount then
255
+     pqueue[k] = nil
256
+    else
257
+     pqueue[k][5]=computer.uptime()+cfg.retry
258
+     pqueue[k][6]=pqueue[k][6]+1
259
+    end
260
+   end
261
+  end
262
+ end
263
+
264
+ listeners["modem_message"]=processPacket
265
+ listeners["net_send"]=queuePacket
266
+ if _OSVERSION:sub(1,6) == "OpenOS" then
267
+  event.listen("modem_message",processPacket)
268
+  print("Started packet listening daemon: "..tostring(processPacket))
269
+  event.listen("net_send",queuePacket)
270
+  print("Started packet queueing daemon: "..tostring(queuePacket))
271
+  timers[#timers+1]=event.timer(0,packetPusher,math.huge)
272
+  print("Started packet pusher: "..tostring(timers[#timers]))
273
+ end
274
+ if _OSVERSION:sub(1,8) == "KittenOS" then
275
+ neo.requireAccess("r.svc.minitel","minitel daemon")(function(pkg,pid,sendSig)
276
+  processes[pid] = sendSig
277
+  return {["sendPacket"]=queuePacket}
278
+ end)
279
+ end
280
+ 
281
+ if _OSVERSION:sub(1,8) == "KittenOS" or _OSVERSION:sub(1,7) == "PsychOS" then
282
+  while true do
283
+   local ev = {coroutine.yield()}
284
+   packetPusher()
285
+   pruneCache()
286
+   if ev[1] == "k.procdie" then
287
+    processes[ev[3]] = nil
288
+   end
289
+   if listeners[ev[1]] then
290
+    pcall(listeners[ev[1]],table.unpack(ev))
291
+   end
292
+  end
293
+ end
294
+end
295
+
296
+if _OSVERSION:sub(1,6) ~= "OpenOS" then
297
+ start()
298
+end

Loading…
Cancel
Save