Advertisement
1lann

Rednet Spam Monitor server_api

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