OpenComputers Von Neumann Machine Programs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
6.0 KiB

local component = require("component")
local async = require("async")
local fs = require("filesystem")
local io = require("io")
local serial = require("serialization")
local ll = require("liblua")
local modem = component.proxy(component.list("modem")())
local dns = {
PORT = 53,
RECORDS = "/etc/dns/records"
}
-- Local function for creating directory
function createRecordsDir(fileName)
local configsExist = true
local dir = fs.path(fileName)
if fs.exists(dir) and not fs.isDirectory(dir) then
fs.remove(dir)
end
if not fs.exists(dir) then
fs.makeDirectory(dir)
return true
end
return false
end
-- Gets an existing server config or creates one if it doesn't exist
function dns.getRecords(fileName)
if fileName == nil then fileName = dns.RECORDS end
local recordsExist = true
if not createRecordsDir(fileName) then
if fs.isDirectory(fileName) then
fs.remove(fileName)
end
if not fs.exists(fileName) then
recordsExist = false
end
else
recordsExist = false
end
records = {}
if not recordsExist then
print("Creating records file since it doesn't exist")
local f = io.open(fileName, "w")
f:close()
return records
else
f = io.open(fileName, "r")
while true do
local line = f:read()
if line ~= nil then
local t = ll.split(line)
-- Records file format: <address> <host>>
records[t[2]] = t[1]
else
break
end
end
f:close()
end
return records
end
-- Helper to get a single record
function getRecord(data, records)
if records == nil then
records = dns.getRecords()
end
-- Assume most people will resolve not reverse, so resolve is fast, reverse is slow
local record = nil
if data[3] == "resolve" then
record = records[data[4]]
else
for k, v in pairs(records) do
if data[4] == v then
record = k
end
end
end
return record
end
function dns.startServer(port, nonblocking, cacheRecords)
-- Make sure ports aren't invalid
if type(port) ~= "number" then
port = dns.PORT
end
-- Remember to open the port
modem.open(port)
-- Filter function for getting dns server messages
function dns.serverMsg(name, ...)
local t = {...}
-- Check proper start to client dns query
if name ~= "modem_message" or t[3] ~= port or t[5] ~= "dns" or t[6] ~= "client" then
return false
end
-- Check resolve or reverse
if t[7] ~= "resolve" and t[7] ~= "reverse" then
return false
end
-- Make sure it's at least of type string if not going to check valid uuid
if type(t[8]) ~= "string" then
return false
end
return true
end
print("Starting up DNS server")
local records = nil
if cacheRecords == nil or cacheRecords then
records = dns.getRecords()
end
while true do
local pkt, err = {async.pullFiltered(dns.serverMsg, not nonblocking)}
print(#pkt)
if #pkt == 0 then
return pkt, err
elseif pkt[1] == "interrupted" then
print("Interrupted")
return pkt, err
end
local raddr, port = pkt[3], pkt[4]
local data = ll.slice(pkt, 6)
print("Received request for "..data[4].." from "..raddr)
modem.send(raddr, port, "dns", "server", "record", getRecord(data, records))
end
end
-- local helper function for doing resolving
function query(host, useCache, queryType, nonblocking)
if useCache == nil then
useCache = true
end
if queryType == nil then
queryType = "resolve"
end
record = nil
if useCache then
data = {"dns", "client", queryType, host}
record = getRecord(data)
if record ~= nil then
return record
end
end
-- Make sure ports aren't invalid
local port = dns.PORT
-- Don't forget to open the port
modem.open(port)
-- Filter function for getting dns client messages
function dns.clientMsg(name, ...)
local t = {...}
-- Check proper start to server dns response
if name ~= "modem_message"
or t[3] ~= port
or t[5] ~= "dns"
or t[6] ~= "server"
or t[7] ~= "record" then
return false
end
-- Make sure it's at least of type string if not going to check valid uuid
if type(t[8]) ~= "string" and t[8] ~= nil then
return false
end
return true
end
-- Throw error if file can't be opened
local f, err = io.open("/etc/resolv.cfg", "r")
if f == nil then
return nil, err
end
data = ""
while true do
local line = f:read()
if line ~= nil then
data = data..line
else
break
end
end
f:close()
local servAddr = serial.unserialize(data).dns
if servAddr == "" then
return nil, "No DNS server specified\nPlease edit /etc/resolv.cfg or set one on your dhcp server and run dhclient again"
end
modem.send(servAddr, port, "dns", "client", queryType, host)
-- Add timeout so we don't wait forever
local timeout = 3
-- Make sure to not listen to random people and only the server requested to
local pkt = nil
repeat
pkt = {async.pullFiltered(timeout, dns.clientMsg, not nonblocking)}
if #pkt == 0 then
return nil, "Server timeout"
end
until (pkt[3] == servAddr)
-- Return the record
local data = ll.slice(pkt, 6)
if #data == 4 then
record = data[4]
end
f = io.open(dns.RECORDS, "a")
if queryType == "resolve" then
f:write(record.."\t"..host.."\n")
else
f:write(host.."\t"..record)
end
f:close()
return record
end
-- Look up a record
function dns.query(host, useCache, nonblocking)
return query(host, useCache, "resolve", nonblocking)
end
-- Reverse look up a record
function dns.revQuery(host, useCache, nonblocking)
return query(host, useCache, "reverse", nonblocking)
end
-- wget -f http://localhost:8080/lib/dns.lua lib/dns.lua
return dns