Advertisement
1lann

Rednet Spam Monitor standalone

Nov 10th, 2016
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.51 KB | None | 0 0
  1. local expiry = 3
  2. local freshness = 60
  3. local threshold = 10
  4. local expired = 120
  5.  
  6. local operatingChannel = 26743
  7. local messageList = {}
  8. local activeSpam = {}
  9. local locationDatabase = {}
  10. local profileQueue = {}
  11. local checkTimer = os.startTimer(1)
  12.  
  13. local modem = peripheral.wrap("top")
  14.  
  15. local findSpammers = function()
  16.     local potentialSpammers = {}
  17.     local topTargets = {}
  18.     for k, message in pairs(messageList) do
  19.         if message.expiry > os.clock() then
  20.             local origin = tostring(message.origin)
  21.             if not potentialSpammers[origin] then
  22.                 potentialSpammers[origin] = {count = 0, fiveIDs = {}}
  23.                 topTargets[origin] = {}
  24.             end
  25.             if not topTargets[origin][tostring(message.target)] then
  26.                 topTargets[origin][tostring(message.target)] = 0
  27.             end
  28.             topTargets[origin][tostring(message.target)] = topTargets[origin][tostring(message.target)] + 1
  29.             potentialSpammers[origin].count = potentialSpammers[origin].count + 1
  30.             table.insert(potentialSpammers[origin].fiveIDs, message.id)
  31.             if #potentialSpammers[origin].fiveIDs > 5 then
  32.                 table.remove(potentialSpammers[origin].fiveIDs, 1)
  33.             end
  34.         end
  35.     end
  36.  
  37.     for origin, v in pairs(potentialSpammers) do
  38.         if v.count > threshold then
  39.             local highestHit = 0
  40.             local topTarget = nil
  41.             for target, hit in pairs(topTargets[origin]) do
  42.                 if hit > highestHit then
  43.                     highestHit = hit
  44.                     topTarget = target
  45.                 end
  46.             end
  47.             table.insert(profileQueue, {origin = origin, id = v.fiveIDs[1], target = topTarget, time = os.clock()})
  48.         end
  49.     end
  50.  
  51.     os.queueEvent("ddosmonitor-profile-constructor-bump")
  52. end
  53.  
  54. local function trilaterate( A, B, C )
  55.     print(textutils.serialize(A))
  56.     print(textutils.serialize(B))
  57.     print(textutils.serialize(C))
  58.     local a2b = B.vPosition - A.vPosition
  59.     local a2c = C.vPosition - A.vPosition
  60.        
  61.     if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
  62.         return nil
  63.     end
  64.    
  65.     local d = a2b:length()
  66.     local ex = a2b:normalize( )
  67.     local i = ex:dot( a2c )
  68.     local ey = (a2c - (ex * i)):normalize()
  69.     local j = ey:dot( a2c )
  70.     local ez = ex:cross( ey )
  71.  
  72.     local r1 = A.nDistance
  73.     local r2 = B.nDistance
  74.     local r3 = C.nDistance
  75.        
  76.     local x = (r1*r1 - r2*r2 + d*d) / (2*d)
  77.     local y = (r1*r1 - r3*r3 - x*x + (x-i)*(x-i) + j*j) / (2*j)
  78.        
  79.     local result = A.vPosition + (ex * x) + (ey * y)
  80.  
  81.     local zSquared = r1*r1 - x*x - y*y
  82.     if zSquared > 0 then
  83.         local z = math.sqrt( zSquared )
  84.         local result1 = result + (ez * z)
  85.         local result2 = result - (ez * z)
  86.        
  87.         local rounded1, rounded2 = result1:round( nRoundSize ), result2:round( nRoundSize )
  88.         if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
  89.             return rounded1, rounded2
  90.         else
  91.             return rounded1
  92.         end
  93.     end
  94.     return result:round( nRoundSize )
  95.    
  96. end
  97.  
  98. local function narrow( p1, p2, fix )
  99.     local dist1 = math.abs( (p1 - fix.vPosition):length() - fix.nDistance )
  100.     local dist2 = math.abs( (p2 - fix.vPosition):length() - fix.nDistance )
  101.    
  102.     if math.abs(dist1 - dist2) < 0.01 then
  103.         return p1, p2
  104.     elseif dist1 < dist2 then
  105.         return p1:round( nRoundSize )
  106.     else
  107.         return p2:round( nRoundSize )
  108.     end
  109. end
  110.  
  111. local getLocation = function(locations)
  112.     -- PLEASE DON'T KILL ME FOR USING THESE VARIABLE NAMES, IT WAS WRITTEN BY DAN200
  113.     -- Code taken from /rom/apis/gps by dan200
  114.     local fixes = {}
  115.     for k, location in pairs(locations) do
  116.         table.insert(fixes, {vPosition = vector.new( location.x, location.y, location.z ), nDistance = location.dist})
  117.     end
  118.  
  119.     local pos1, pos2 = trilaterate(fixes[1], fixes[2], fixes[3])
  120.     if #fixes > 3 then
  121.         for i = 4, #fixes do
  122.             pos1, pos2 = narrow(pos1, pos2, fixes[i])
  123.         end
  124.     end
  125.  
  126.     if pos1 and pos2 then
  127.         return "Possible locations: x: ".. pos1.x.." y: ".. pos1.y.." z: "..pos1.z, "or x: "..pos2.x.." y: "..pos2.y.." z: "..pos2.z
  128.     elseif pos1 then
  129.         return "Position: x: ".. pos1.x.." y: ".. pos1.y.." z: "..pos1.z
  130.     else
  131.         return "Unknown"
  132.     end
  133. end
  134.  
  135. local isInMessageList = function(id)
  136.     for k,v in pairs(messageList) do
  137.         if v.id == id then
  138.             return true
  139.         end
  140.     end
  141.     return false
  142. end
  143.  
  144. local ddosMonitor = function()
  145.     modem.open(rednet.CHANNEL_REPEAT)
  146.  
  147.     while true do
  148.         local event, side, channel, reply, message, dist = os.pullEventRaw()
  149.         if event == "modem_message" and channel == rednet.CHANNEL_REPEAT and type(message) == "table" then
  150.             if message.nMessageID and message.nRecipient and not isInMessageList(message.nMessageID) then
  151.                 table.insert(messageList, {origin = reply, target = message.nRecipient, id = message.nMessageID, expiry = os.clock() + expiry})
  152.                 if #messageList > 100 then
  153.                     table.remove(messageList, 1)
  154.                 end
  155.             end
  156.         elseif event == "timer" and side == checkTimer then
  157.             findSpammers()
  158.             checkTimer = os.startTimer(1)
  159.         end
  160.     end
  161. end
  162.  
  163. local profileConstructor = function()
  164.     while true do
  165.         os.pullEvent()
  166.         while #profileQueue > 0 do
  167.             local currentProfile = profileQueue[1]
  168.             table.remove(profileQueue, 1)
  169.             local target = ""
  170.             if tonumber(currentProfile.target) == rednet.CHANNEL_BROADCAST then
  171.                 target = "Broadcast"
  172.             else
  173.                 target = currentProfile.target
  174.             end
  175.             if locationDatabase[currentProfile.origin] and (locationDatabase[currentProfile.origin].expiry > os.clock()) then
  176.                 cachedLocation = locationDatabase[currentProfile.origin]
  177.                 activeSpam[currentProfile.origin] = {active = os.clock(), locationA = cachedLocation.locationA, locationB = cachedLocation.locationB, target = target}
  178.             else
  179.                 local responses = {}
  180.  
  181.                 modem.open(operatingChannel)
  182.                 modem.transmit(operatingChannel, operatingChannel, {
  183.                     ddosMonitor = "request",
  184.                     messageID = currentProfile.id
  185.                 })
  186.  
  187.                 local timeout = os.startTimer(2)
  188.                 while true do
  189.                     local event, side, channel, reply, message = os.pullEventRaw()
  190.                     if event == "modem_message" and type(message) == "table" and message.ddosMonitor and message.ddosMonitor == "response" then
  191.                         if message.x and message.y and message.z and message.dist and message.id and message.id == currentProfile.id then
  192.                             table.insert(responses, {x = message.x, y = message.y, z = message.z, dist = message.dist})
  193.                         end
  194.                     elseif event == "timer" and side == timeout then
  195.                         if #responses > 2 then
  196.                             locationA, locationB = getLocation(responses)
  197.                             locationDatabase[currentProfile.origin] = {locationA = locationA, locationB = locationB, expiry = os.clock() + freshness}
  198.                             activeSpam[currentProfile.origin] = {active = os.clock(), locationA = locationA, locationB = locationB, target = target}
  199.                         else
  200.                             activeSpam[currentProfile.origin] = {active = os.clock(), locationA = "Unknown", target = target}
  201.                         end
  202.                         break
  203.                     end
  204.                 end
  205.             end
  206.         end
  207.     end
  208. end
  209.  
  210. local getData = function()
  211.     local returnTable = {}
  212.     local spamCopy = {}
  213.     for k,v in pairs(activeSpam) do
  214.         spamCopy[k] = v
  215.     end
  216.     for i = 1, 3 do
  217.         local mostRecent = math.huge
  218.         local mostRecentID = nil
  219.         for id, v in pairs(spamCopy) do
  220.             if ((os.clock() - v.active) < expired) and (os.clock() - v.active) < mostRecent then
  221.                 mostRecent = (os.clock() - v.active)
  222.                 mostRecentID = id
  223.             end
  224.         end
  225.         if mostRecentID then
  226.             table.insert(returnTable, {id = mostRecentID, time = mostRecent,
  227.             locationA = spamCopy[mostRecentID].locationA, locationB = spamCopy[mostRecentID].locationB})
  228.             spamCopy[mostRecentID] = nil
  229.         end
  230.     end
  231.  
  232.     local a = textutils.serialize(returnTable)
  233.     return a
  234. end
  235.  
  236. local main = function()
  237.     while true do
  238.         parallel.waitForAny(ddosMonitor, profileConstructor, function()
  239.             while true do
  240.                 print(getData())
  241.                 sleep(3)
  242.             end
  243.         end)
  244.     end
  245. end
  246.  
  247. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement