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.

258 lines
6.0 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. local component = require("component")
  2. local async = require("async")
  3. local fs = require("filesystem")
  4. local io = require("io")
  5. local serial = require("serialization")
  6. local ll = require("liblua")
  7. local modem = component.proxy(component.list("modem")())
  8. local dns = {
  9. PORT = 53,
  10. RECORDS = "/etc/dns/records"
  11. }
  12. -- Local function for creating directory
  13. function createRecordsDir(fileName)
  14. local configsExist = true
  15. local dir = fs.path(fileName)
  16. if fs.exists(dir) and not fs.isDirectory(dir) then
  17. fs.remove(dir)
  18. end
  19. if not fs.exists(dir) then
  20. fs.makeDirectory(dir)
  21. return true
  22. end
  23. return false
  24. end
  25. -- Gets an existing server config or creates one if it doesn't exist
  26. function dns.getRecords(fileName)
  27. if fileName == nil then fileName = dns.RECORDS end
  28. local recordsExist = true
  29. if not createRecordsDir(fileName) then
  30. if fs.isDirectory(fileName) then
  31. fs.remove(fileName)
  32. end
  33. if not fs.exists(fileName) then
  34. recordsExist = false
  35. end
  36. else
  37. recordsExist = false
  38. end
  39. records = {}
  40. if not recordsExist then
  41. print("Creating records file since it doesn't exist")
  42. local f = io.open(fileName, "w")
  43. f:close()
  44. return records
  45. else
  46. f = io.open(fileName, "r")
  47. while true do
  48. local line = f:read()
  49. if line ~= nil then
  50. local t = ll.split(line)
  51. -- Records file format: <address> <host>>
  52. records[t[2]] = t[1]
  53. else
  54. break
  55. end
  56. end
  57. f:close()
  58. end
  59. return records
  60. end
  61. -- Helper to get a single record
  62. function getRecord(data, records)
  63. if records == nil then
  64. records = dns.getRecords()
  65. end
  66. -- Assume most people will resolve not reverse, so resolve is fast, reverse is slow
  67. local record = nil
  68. if data[3] == "resolve" then
  69. record = records[data[4]]
  70. else
  71. for k, v in pairs(records) do
  72. if data[4] == v then
  73. record = k
  74. end
  75. end
  76. end
  77. return record
  78. end
  79. function dns.startServer(port, nonblocking, cacheRecords)
  80. -- Make sure ports aren't invalid
  81. if type(port) ~= "number" then
  82. port = dns.PORT
  83. end
  84. -- Remember to open the port
  85. modem.open(port)
  86. -- Filter function for getting dns server messages
  87. function dns.serverMsg(name, ...)
  88. local t = {...}
  89. -- Check proper start to client dns query
  90. if name ~= "modem_message" or t[3] ~= port or t[5] ~= "dns" or t[6] ~= "client" then
  91. return false
  92. end
  93. -- Check resolve or reverse
  94. if t[7] ~= "resolve" and t[7] ~= "reverse" then
  95. return false
  96. end
  97. -- Make sure it's at least of type string if not going to check valid uuid
  98. if type(t[8]) ~= "string" then
  99. return false
  100. end
  101. return true
  102. end
  103. print("Starting up DNS server")
  104. local records = nil
  105. if cacheRecords == nil or cacheRecords then
  106. records = dns.getRecords()
  107. end
  108. while true do
  109. local pkt, err = {async.pullFiltered(dns.serverMsg, not nonblocking)}
  110. print(#pkt)
  111. if #pkt == 0 then
  112. return pkt, err
  113. elseif pkt[1] == "interrupted" then
  114. print("Interrupted")
  115. return pkt, err
  116. end
  117. local raddr, port = pkt[3], pkt[4]
  118. local data = ll.slice(pkt, 6)
  119. print("Received request for "..data[4].." from "..raddr)
  120. modem.send(raddr, port, "dns", "server", "record", getRecord(data, records))
  121. end
  122. end
  123. -- local helper function for doing resolving
  124. function query(host, useCache, queryType, nonblocking)
  125. if useCache == nil then
  126. useCache = true
  127. end
  128. if queryType == nil then
  129. queryType = "resolve"
  130. end
  131. record = nil
  132. if useCache then
  133. data = {"dns", "client", queryType, host}
  134. record = getRecord(data)
  135. if record ~= nil then
  136. return record
  137. end
  138. end
  139. -- Make sure ports aren't invalid
  140. local port = dns.PORT
  141. -- Don't forget to open the port
  142. modem.open(port)
  143. -- Filter function for getting dns client messages
  144. function dns.clientMsg(name, ...)
  145. local t = {...}
  146. -- Check proper start to server dns response
  147. if name ~= "modem_message"
  148. or t[3] ~= port
  149. or t[5] ~= "dns"
  150. or t[6] ~= "server"
  151. or t[7] ~= "record" then
  152. return false
  153. end
  154. -- Make sure it's at least of type string if not going to check valid uuid
  155. if type(t[8]) ~= "string" and t[8] ~= nil then
  156. return false
  157. end
  158. return true
  159. end
  160. -- Throw error if file can't be opened
  161. local f, err = io.open("/etc/resolv.cfg", "r")
  162. if f == nil then
  163. return nil, err
  164. end
  165. data = ""
  166. while true do
  167. local line = f:read()
  168. if line ~= nil then
  169. data = data..line
  170. else
  171. break
  172. end
  173. end
  174. f:close()
  175. local servAddr = serial.unserialize(data).dns
  176. if servAddr == "" then
  177. return nil, "No DNS server specified\nPlease edit /etc/resolv.cfg or set one on your dhcp server and run dhclient again"
  178. end
  179. modem.send(servAddr, port, "dns", "client", queryType, host)
  180. -- Add timeout so we don't wait forever
  181. local timeout = 3
  182. -- Make sure to not listen to random people and only the server requested to
  183. local pkt = nil
  184. repeat
  185. pkt = {async.pullFiltered(timeout, dns.clientMsg, not nonblocking)}
  186. if #pkt == 0 then
  187. return nil, "Server timeout"
  188. end
  189. until (pkt[3] == servAddr)
  190. -- Return the record
  191. local data = ll.slice(pkt, 6)
  192. if #data == 4 then
  193. record = data[4]
  194. end
  195. f = io.open(dns.RECORDS, "a")
  196. if queryType == "resolve" then
  197. f:write(record.."\t"..host.."\n")
  198. else
  199. f:write(host.."\t"..record)
  200. end
  201. f:close()
  202. return record
  203. end
  204. -- Look up a record
  205. function dns.query(host, useCache, nonblocking)
  206. return query(host, useCache, "resolve", nonblocking)
  207. end
  208. -- Reverse look up a record
  209. function dns.revQuery(host, useCache, nonblocking)
  210. return query(host, useCache, "reverse", nonblocking)
  211. end
  212. -- wget -f http://localhost:8080/lib/dns.lua lib/dns.lua
  213. return dns