Advertisement
1lann

lostturtle

Apr 15th, 2014
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.99 KB | None | 0 0
  1. --
  2. --  Firewolf
  3. --  Made by GravityScore and 1lann
  4. --
  5.  
  6. -- 1.58 Wrapper
  7.  
  8. -- Rednet
  9.  
  10. local rednet = {}
  11.  
  12. rednet.CHANNEL_BROADCAST = 65535
  13. rednet.CHANNEL_REPEAT = 65533
  14.  
  15. local tReceivedMessages = {}
  16. local tReceivedMessageTimeouts = {}
  17. local tHostnames = {}
  18.  
  19. function rednet.open( sModem )
  20.     if type( sModem ) ~= "string" then
  21.         error( "expected string", 2 )
  22.     end
  23.     if peripheral.getType( sModem ) ~= "modem" then
  24.         error( "No such modem: "..sModem, 2 )
  25.     end
  26.     peripheral.call( sModem, "open", os.getComputerID() )
  27.     peripheral.call( sModem, "open", rednet.CHANNEL_BROADCAST )
  28. end
  29.  
  30. function rednet.close( sModem )
  31.     if sModem then
  32.         -- Close a specific modem
  33.         if type( sModem ) ~= "string" then
  34.             error( "expected string", 2 )
  35.         end
  36.         if peripheral.getType( sModem ) ~= "modem" then
  37.             error( "No such modem: "..sModem, 2 )
  38.         end
  39.         peripheral.call( sModem, "close", os.getComputerID() )
  40.         peripheral.call( sModem, "close", rednet.CHANNEL_BROADCAST )
  41.     else
  42.         -- Close all modems
  43.         for n,sModem in ipairs( peripheral.getNames() ) do
  44.             if rednet.isOpen( sModem ) then
  45.                 rednet.close( sModem )
  46.             end
  47.         end
  48.     end
  49. end
  50.  
  51. function rednet.isOpen( sModem )
  52.     if sModem then
  53.         -- Check if a specific modem is open
  54.         if type( sModem ) ~= "string" then
  55.             error( "expected string", 2 )
  56.         end
  57.         if peripheral.getType( sModem ) == "modem" then
  58.             return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", rednet.CHANNEL_BROADCAST )
  59.         end
  60.     else
  61.         -- Check if any modem is open
  62.         for n,sModem in ipairs( peripheral.getNames() ) do
  63.             if rednet.isOpen( sModem ) then
  64.                 return true
  65.             end
  66.         end
  67.     end
  68.     return false
  69. end
  70.  
  71. function rednet.send( nRecipient, message, sProtocol )
  72.     -- Generate a (probably) unique message ID
  73.     -- We could do other things to guarantee uniqueness, but we really don't need to
  74.     -- Store it to ensure we don't get our own messages back
  75.     local nMessageID = math.random( 1, 2147483647 )
  76.     tReceivedMessages[ nMessageID ] = true
  77.     tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID
  78.  
  79.     -- Create the message
  80.     local nReplyChannel = os.getComputerID()
  81.     local tMessage = {
  82.         nMessageID = nMessageID,
  83.         nRecipient = nRecipient,
  84.         message = message,
  85.         sProtocol = sProtocol,
  86.     }
  87.  
  88.     if nRecipient == os.getComputerID() then
  89.         -- Loopback to ourselves
  90.         os.queueEvent( "rednet_message", nReplyChannel, message, sProtocol )
  91.  
  92.     else
  93.         -- Send on all open modems, to the target and to repeaters
  94.         local sent = false
  95.         for n,sModem in ipairs( peripheral.getNames() ) do
  96.             if rednet.isOpen( sModem ) then
  97.                 peripheral.call( sModem, "transmit", nRecipient, nReplyChannel, tMessage );
  98.                 peripheral.call( sModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage );
  99.                 sent = true
  100.             end
  101.         end
  102.     end
  103. end
  104.  
  105. function rednet.broadcast( message, sProtocol )
  106.     rednet.send( rednet.CHANNEL_BROADCAST, message, sProtocol )
  107. end
  108.  
  109. function rednet.receive( sProtocolFilter, nTimeout )
  110.     -- The parameters used to be ( nTimeout ), detect this case for backwards compatibility
  111.     if type(sProtocolFilter) == "number" and nTimeout == nil then
  112.         sProtocolFilter, nTimeout = nil, sProtocolFilter
  113.     end
  114.  
  115.     -- Start the timer
  116.     local timer = nil
  117.     local sFilter = nil
  118.     if nTimeout then
  119.         timer = os.startTimer( nTimeout )
  120.         sFilter = nil
  121.     else
  122.         sFilter = "rednet_message"
  123.     end
  124.  
  125.     -- Wait for events
  126.     while true do
  127.         local sEvent, p1, p2, p3 = os.pullEvent( sFilter )
  128.         if sEvent == "rednet_message" then
  129.             -- Return the first matching rednet_message
  130.             local nSenderID, message, sProtocol = p1, p2, p3
  131.             if sProtocolFilter == nil or sProtocol == sProtocolFilter then
  132.                 return nSenderID, message, sProtocol
  133.             end
  134.         elseif sEvent == "timer" then
  135.             -- Return nil if we timeout
  136.             if p1 == timer then
  137.                 return nil
  138.             end
  139.         end
  140.     end
  141. end
  142.  
  143. function rednet.host( sProtocol, sHostname )
  144.     if type( sProtocol ) ~= "string" or type( sHostname ) ~= "string" then
  145.         error( "expected string, string", 2 )
  146.     end
  147.     if sHostname == "localhost" then
  148.         error( "Reserved hostname", 2 )
  149.     end
  150.     if tHostnames[ sProtocol ] ~= sHostname then
  151.         if rednet.lookup( sProtocol, sHostname ) ~= nil then
  152.             error( "Hostname in use", 2 )
  153.         end
  154.         tHostnames[ sProtocol ] = sHostname
  155.     end
  156. end
  157.  
  158. function rednet.unhost( sProtocol )
  159.     if type( sProtocol ) ~= "string" then
  160.         error( "expected string", 2 )
  161.     end
  162.     tHostnames[ sProtocol ] = nil
  163. end
  164.  
  165. function rednet.lookup( sProtocol, sHostname )
  166.     if type( sProtocol ) ~= "string" then
  167.         error( "expected string", 2 )
  168.     end
  169.  
  170.     -- Build list of host IDs
  171.     local tResults = nil
  172.     if sHostname == nil then
  173.         tResults = {}
  174.     end
  175.  
  176.     -- Check localhost first
  177.     if tHostnames[ sProtocol ] then
  178.         if sHostname == nil then
  179.             table.insert( tResults, os.getComputerID() )
  180.         elseif sHostname == "localhost" or sHostname == tHostnames[ sProtocol ] then
  181.             return os.getComputerID()
  182.         end
  183.     end
  184.  
  185.     if not rednet.isOpen() then
  186.         if tResults then
  187.             return unpack( tResults )
  188.         end
  189.         return nil
  190.     end
  191.  
  192.     -- Broadcast a lookup packet
  193.     rednet.broadcast( {
  194.         sType = "lookup",
  195.         sProtocol = sProtocol,
  196.         sHostname = sHostname,
  197.     }, "dns" )
  198.  
  199.     -- Start a timer
  200.     local timer = os.startTimer( 2 )
  201.  
  202.     -- Wait for events
  203.     while true do
  204.         local event, p1, p2, p3 = os.pullEvent()
  205.         if event == "rednet_message" then
  206.             -- Got a rednet message, check if it's the response to our request
  207.             local nSenderID, tMessage, sMessageProtocol = p1, p2, p3
  208.             if sMessageProtocol == "dns" and tMessage.sType == "lookup response" then
  209.                 if tMessage.sProtocol == sProtocol then
  210.                     if sHostname == nil then
  211.                         table.insert( tResults, nSenderID )
  212.                     elseif tMessage.sHostname == sHostname then
  213.                         return nSenderID
  214.                     end
  215.                 end
  216.             end
  217.         else
  218.             -- Got a timer event, check it's the end of our timeout
  219.             if p1 == timer then
  220.                 break
  221.             end
  222.         end
  223.     end
  224.     if tResults then
  225.         return unpack( tResults )
  226.     end
  227.     return nil
  228. end
  229.  
  230. local bRunning = false
  231. function rednet.run()
  232.     if bRunning then
  233.         error( "rednet is already running", 2 )
  234.     end
  235.     bRunning = true
  236.    
  237.     while bRunning do
  238.         local sEvent, p1, p2, p3, p4 = os.pullEventRaw()
  239.         if sEvent == "modem_message" then
  240.             -- Got a modem message, process it and add it to the rednet event queue
  241.             local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4
  242.             if rednet.isOpen( sModem ) and ( nChannel == os.getComputerID() or nChannel == rednet.CHANNEL_BROADCAST ) then
  243.                 if type( tMessage ) == "table" and tMessage.nMessageID then
  244.                     if not tReceivedMessages[ tMessage.nMessageID ] then
  245.                         tReceivedMessages[ tMessage.nMessageID ] = true
  246.                         tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID
  247.                         os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol )
  248.                     end
  249.                 end
  250.             end
  251.         elseif sEvent == "rednet_message" then
  252.             -- Got a rednet message (queued from above), respond to dns lookup
  253.             local nSenderID, tMessage, sProtocol = p1, p2, p3
  254.             if sProtocol == "dns" and tMessage.sType == "lookup" then
  255.                 local sHostname = tHostnames[ tMessage.sProtocol ]
  256.                 if sHostname ~= nil and (tMessage.sHostname == nil or tMessage.sHostname == sHostname) then
  257.                     rednet.send( nSenderID, {
  258.                         sType = "lookup response",
  259.                         sHostname = sHostname,
  260.                         sProtocol = tMessage.sProtocol,
  261.                     }, "dns" )
  262.                 end
  263.             end
  264.  
  265.         elseif sEvent == "timer" then
  266.             -- Got a timer event, use it to clear the event queue
  267.             local nTimer = p1
  268.             local nMessage = tReceivedMessageTimeouts[ nTimer ]
  269.             if nMessage then
  270.                 tReceivedMessageTimeouts[ nTimer ] = nil
  271.                 tReceivedMessages[ nMessage ] = nil
  272.             end
  273.         end
  274.     end
  275. end
  276.  
  277. --    Variables
  278.  
  279.  
  280. local version = "3.0"
  281. local build = 0
  282.  
  283. local w, h = term.getSize()
  284.  
  285. local serversFolder = "/fw_servers"
  286. local indexFileName = "index"
  287.  
  288. local sides = {}
  289.  
  290. local publicDnsChannel = 9999
  291. local publicRespChannel = 9998
  292. local responseID = 41738
  293.  
  294. local DNSRequestTag = "--@!FIREWOLF-LIST!@--"
  295. local DNSResponseTag = "--@!FIREWOLF-DNSRESP!@--"
  296. local connectTag = "--@!FIREWOLF-CONNECT!@--"
  297. local disconnectTag = "--@!FIREWOLF-DISCONNECT!@--"
  298. local receiveTag = "--@!FIREWOLF-RECEIVE!@--"
  299. local headTag = "--@!FIREWOLF-HEAD!@--"
  300. local bodyTag = "--@!FIREWOLF-BODY!@--"
  301. local initiateTag = "--@!FIREWOLF-INITIATE!@--"
  302. local protocolTag = "--@!FIREWOLF-REDNET-PROTOCOL!@--"
  303.  
  304. local initiatePattern = "^%-%-@!FIREWOLF%-INITIATE!@%-%-(.+)"
  305. local retrievePattern = "^%-%-@!FIREWOLF%-FETCH!@%-%-(.+)"
  306.  
  307. local theme = {}
  308.  
  309. local colorTheme = {
  310.     background = colors.gray,
  311.     accent = colors.red,
  312.     subtle = colors.orange,
  313.  
  314.     lightText = colors.gray,
  315.     text = colors.white,
  316.     errorText = colors.red,
  317. }
  318.  
  319. local grayscaleTheme = {
  320.     background = colors.black,
  321.     accent = colors.black,
  322.     subtle = colors.black,
  323.  
  324.     lightText = colors.white,
  325.     text = colors.white,
  326.     errorText = colors.white,
  327. }
  328.  
  329. local default404 = [[
  330. local function center(text)
  331.     local x, y = term.getCursorPos()
  332.     term.setCursorPos(math.floor(w / 2 - text:len() / 2) + (text:len() % 2 == 0 and 1 or 0), y)
  333.     term.write(text)
  334.     term.setCursorPos(1, y + 1)
  335. end
  336.  
  337. term.setTextColor(colors.white)
  338. term.setBackgroundColor(colors.gray)
  339. term.clear()
  340.  
  341. term.setCursorPos(1, 4)
  342. center("Error 404")
  343. print("\n")
  344. center("The page could not be found.")
  345. ]]
  346.  
  347.  
  348.  
  349. --    RC4
  350. --    Implementation by AgentE382
  351.  
  352.  
  353. local cryptWrapper = function(plaintext, salt)
  354.     local key = type(salt) == "table" and {unpack(salt)} or {string.byte(salt, 1, #salt)}
  355.     local S = {}
  356.     for i = 0, 255 do
  357.         S[i] = i
  358.     end
  359.  
  360.     local j, keylength = 0, #key
  361.     for i = 0, 255 do
  362.         j = (j + S[i] + key[i % keylength + 1]) % 256
  363.         S[i], S[j] = S[j], S[i]
  364.     end
  365.  
  366.     local i = 0
  367.     j = 0
  368.     local chars, astable = type(plaintext) == "table" and {unpack(plaintext)} or {string.byte(plaintext, 1, #plaintext)}, false
  369.  
  370.     for n = 1, #chars do
  371.         i = (i + 1) % 256
  372.         j = (j + S[i]) % 256
  373.         S[i], S[j] = S[j], S[i]
  374.         chars[n] = bit.bxor(S[(S[i] + S[j]) % 256], chars[n])
  375.         if chars[n] > 127 or chars[n] == 13 then
  376.             astable = true
  377.         end
  378.     end
  379.  
  380.     return astable and chars or string.char(unpack(chars))
  381. end
  382.  
  383.  
  384. local crypt = function(plaintext, salt)
  385.     local resp, msg = pcall(cryptWrapper, plaintext, salt)
  386.     if resp then
  387.         if type(msg) == "table" then
  388.             return textutils.serialize(msg)
  389.         else
  390.             return msg
  391.         end
  392.     else
  393.         return nil
  394.     end
  395. end
  396.  
  397.  
  398. --    Backend
  399.  
  400.  
  401. local setupModem = function()
  402.     for _, v in pairs(redstone.getSides()) do
  403.         if peripheral.getType(v) == "modem" then
  404.             table.insert(sides, v)
  405.         end
  406.     end
  407.  
  408.     if #sides <= 0 then
  409.         error("No modem found!")
  410.     end
  411. end
  412.  
  413.  
  414. local modem = function(func, ...)
  415.             peripheral.call("right", func, ...)
  416.  
  417.     return true
  418. end
  419.  
  420.  
  421. local calculateChannel = function(domain, distance, id)
  422.     local total = 1
  423.  
  424.     if distance then
  425.         id = (id + 3642 * math.pi) % 100000
  426.         if tostring(distance):find("%.") then
  427.             local distProc = (tostring(distance):sub(1, tostring(distance):find("%.") + 1)):gsub("%.", "")
  428.             total = tonumber(distProc..id)
  429.         else
  430.             total = tonumber(distance..id)
  431.         end
  432.     end
  433.  
  434.     for i = 1, #domain do
  435.         total = total * string.byte(domain:sub(i, i))
  436.         if total > 10000000000 then
  437.             total = tonumber(tostring(total):sub(-5, -1))
  438.         end
  439.         while tostring(total):sub(-1, -1) == "0" do
  440.             total = tonumber(tostring(total):sub(1, -2))
  441.         end
  442.     end
  443.  
  444.     return (total % 50000) + 10000
  445. end
  446.  
  447.  
  448. local isSession = function(sessions, channel, distance, id)
  449.     for k, v in pairs(sessions) do
  450.         if v[1] == distance and v[2] == id and v[3] == channel then
  451.             return true
  452.         end
  453.     end
  454.  
  455.     return false
  456. end
  457.  
  458.  
  459. local fetchPage = function(domain, page)
  460.     local part1 = [[
  461.     [br][=][c orange]Oeed's Farming Turtle Info Page[br][br]
  462.     [<][c white][offset 3]Status: ]]
  463.     local part2 = [[
  464.     [br]Next Harvest: ]]
  465.     local part3 = [[
  466.     [br]ID: ]]
  467.     local part4 = [[
  468.     [br]Coordinates: ]]
  469.     local part5 = [[
  470.     [br]Fuel: ]]
  471.     local x,y,z = gps.locate()
  472.     return (part1..farmerStatus..part2..farmerNextHarvest..part3..os.getComputerID()..part4..x..", "..y..", "..z..part5..turtle.getFuelLevel()), "fwml"
  473. end
  474.  
  475. local backend = function(serverURL, onEvent, onMessage)
  476.     local serverChannel = calculateChannel(serverURL)
  477.     local sessions = {}
  478.  
  479.     local receivedMessages = {}
  480.    local receivedMessageTimeouts = {}
  481.  
  482.     modem("open", publicDnsChannel)
  483.     modem("open", serverChannel)
  484.     modem("open", rednet.CHANNEL_REPEAT)
  485.  
  486.             rednet.open("right")
  487.     rednet.host(protocolTag .. serverURL, initiateTag .. serverURL)
  488.  
  489.     onMessage("Hosting rdnt://" .. serverURL)
  490.     onMessage("Listening for incoming requests...")
  491.  
  492.     while true do
  493.         local eventArgs = {os.pullEvent()}
  494.         local event, givenSide, givenChannel, givenID, givenMessage, givenDistance = unpack(eventArgs)
  495.         if event == "modem_message" then
  496.             if givenChannel == serverChannel and givenMessage:match(initiatePattern) == serverURL then
  497.                 modem("transmit", serverChannel, responseID, crypt(connectTag .. serverURL, serverURL .. tostring(givenDistance) .. givenID))
  498.  
  499.                 if #sessions > 50 then
  500.                     modem("close", sessions[#sessions][3])
  501.                     table.remove(sessions)
  502.                 end
  503.  
  504.                 local isInSessions = false
  505.                 for k, v in pairs(sessions) do
  506.                     if v[1] == givenDistance and v[3] == givenID then
  507.                         isInSessions = true
  508.                     end
  509.                 end
  510.  
  511.                 local userChannel = calculateChannel(serverURL, givenDistance, givenID)
  512.                 if not isInSessions then
  513.                     onMessage("[DIRECT] Starting encrypted connection: " .. userChannel)
  514.                     table.insert(sessions, {givenDistance, givenID, userChannel})
  515.                     modem("open", userChannel)
  516.                 else
  517.                     modem("open", userChannel)
  518.                 end
  519.             elseif isSession(sessions, givenChannel, givenDistance, givenID) then
  520.                 local request = crypt(textutils.unserialize(givenMessage), serverURL .. tostring(givenDistance) .. givenID)
  521.                 if request then
  522.                     local domain = request:match(retrievePattern)
  523.                     if domain then
  524.                         local page = domain:match("^[^/]+/(.+)")
  525.                         if not page then
  526.                             page = "index"
  527.                         end
  528.  
  529.                         onMessage("[DIRECT] Requested: /" .. page)
  530.  
  531.                         local contents, language = fetchPage(serverURL, page)
  532.                         if not contents then
  533.                             contents = fetch404(serverURL)
  534.                         end
  535.  
  536.                         local header
  537.                         if language == "fwml" then
  538.                             header = {language = "Firewolf Markup"}
  539.                         else
  540.                             header = {language = "Lua"}
  541.                         end
  542.  
  543.                         modem("transmit", givenChannel, responseID, crypt(headTag .. textutils.serialize(header) .. bodyTag .. contents, serverURL .. tostring(givenDistance) .. givenID))
  544.                     elseif request == disconnectTag then
  545.                         for k, v in pairs(sessions) do
  546.                             if v[2] == givenChannel then
  547.                                 sessions[k] = nil
  548.                                 break
  549.                             end
  550.                         end
  551.  
  552.                         modem("close", givenChannel)
  553.                         onMessage("[DIRECT] Connection closed: " .. givenChannel)
  554.                     end
  555.                 end
  556.             elseif givenChannel == rednet.CHANNEL_REPEAT and type(givenMessage) == "table"
  557.             and givenMessage.nMessageID and givenMessage.nRecipient and
  558.             not receivedMessages[givenMessage.nMessageID] then
  559.                 receivedMessages[givenMessage.nMessageID] = true
  560.                 receivedMessageTimeouts[os.startTimer(30)] = givenMessage.nMessageID
  561.  
  562.                 modem("transmit", rednet.CHANNEL_REPEAT, givenID, givenMessage)
  563.                 modem("transmit", givenMessage.nRecipient, givenID, givenMessage)
  564.             end
  565.         elseif event == "timer" then
  566.             local messageID = receivedMessageTimeouts[givenSide]
  567.             if messageID then
  568.                 receivedMessageTimeouts[givenSide] = nil
  569.                 receivedMessages[messageID] = nil
  570.             end
  571.         elseif event == "rednet_message" then
  572.             if givenID == protocolTag .. serverURL then
  573.                 local id = givenSide
  574.                 local decrypt = crypt(textutils.unserialize(givenChannel), serverURL .. id)
  575.                 if decrypt then
  576.                     local domain = decrypt:match(retrievePattern)
  577.                     if domain then
  578.                         local page = domain:match("^[^/]+/(.+)")
  579.                         if not page then
  580.                             page = "index"
  581.                         end
  582.  
  583.                         onMessage("[REDNET] Requested: /" .. page .. " from " .. id)
  584.  
  585.                         local contents, language = fetchPage(serverURL, page)
  586.                         if not contents then
  587.                             contents = fetch404(serverURL)
  588.                         end
  589.  
  590.                         local header
  591.                         if language == "fwml" then
  592.                             header = {language = "Firewolf Markup"}
  593.                         else
  594.                             header = {language = "Lua"}
  595.                         end
  596.  
  597.                         rednet.send(id, crypt(headTag .. textutils.serialize(header) .. bodyTag .. contents, serverURL .. givenSide), protocolTag .. serverURL)
  598.                     end
  599.                 end
  600.             end
  601.         end
  602.  
  603.         local shouldExit = false
  604.         if shouldExit then
  605.             rednet.unhost(protocolTag .. serverURL, initiateTag .. serverURL)
  606.             break
  607.         end
  608.     end
  609. end
  610.  
  611.  
  612.  
  613. --    Hosting Interface
  614.  
  615.  
  616. local host = function(domain)
  617.  
  618.     local onMessage = function(text)
  619.         return
  620.     end
  621.     backend(domain, onEvent, onMessage)
  622. end
  623.  
  624. local handleError = function(err)
  625.     print(err)
  626.     sleep(2)
  627. end
  628.  
  629.  
  630. local _, err
  631. while true do
  632.     parallel.waitForAny(function() _, err = pcall(host, "oeedturtle.info") end, rednet.run)
  633.  
  634.     if err and not err:lower():find("terminate") then
  635.         handleError(err)
  636.     end
  637.     if err and err:lower():find("terminate") then
  638.         break
  639.     end
  640. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement