Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local expiry = 3
- local freshness = 60
- local threshold = 10
- local expired = 120
- local operatingChannel = 26743
- local messageList = {}
- local activeSpam = {}
- local locationDatabase = {}
- local profileQueue = {}
- local checkTimer = os.startTimer(1)
- local findSpammers = function()
- local potentialSpammers = {}
- local topTargets = {}
- for k, message in pairs(messageList) do
- if message.expiry > os.clock() then
- local origin = tostring(message.origin)
- if not potentialSpammers[origin] then
- potentialSpammers[origin] = {count = 0, fiveIDs = {}}
- topTargets[origin] = {}
- end
- if not topTargets[origin][tostring(message.target)] then
- topTargets[origin][tostring(message.target)] = 0
- end
- topTargets[origin][tostring(message.target)] = topTargets[origin][tostring(message.target)] + 1
- potentialSpammers[origin].count = potentialSpammers[origin].count + 1
- table.insert(potentialSpammers[origin].fiveIDs, message.id)
- if #potentialSpammers[origin].fiveIDs > 5 then
- table.remove(potentialSpammers[origin].fiveIDs, 1)
- end
- end
- end
- for origin, v in pairs(potentialSpammers) do
- if v.count > threshold then
- local highestHit = 0
- local topTarget = nil
- for target, hit in pairs(topTargets[origin]) do
- if hit > highestHit then
- highestHit = hit
- topTarget = target
- end
- end
- table.insert(profileQueue, {origin = origin, id = v.fiveIDs[1], target = topTarget, time = os.clock()})
- end
- end
- os.queueEvent("ddosmonitor-profile-constructor-bump")
- end
- local function trilaterate( A, B, C )
- local a2b = B.vPosition - A.vPosition
- local a2c = C.vPosition - A.vPosition
- if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then
- return nil
- end
- local d = a2b:length()
- local ex = a2b:normalize( )
- local i = ex:dot( a2c )
- local ey = (a2c - (ex * i)):normalize()
- local j = ey:dot( a2c )
- local ez = ex:cross( ey )
- local r1 = A.nDistance
- local r2 = B.nDistance
- local r3 = C.nDistance
- local x = (r1*r1 - r2*r2 + d*d) / (2*d)
- local y = (r1*r1 - r3*r3 - x*x + (x-i)*(x-i) + j*j) / (2*j)
- local result = A.vPosition + (ex * x) + (ey * y)
- local zSquared = r1*r1 - x*x - y*y
- if zSquared > 0 then
- local z = math.sqrt( zSquared )
- local result1 = result + (ez * z)
- local result2 = result - (ez * z)
- local rounded1, rounded2 = result1:round( nRoundSize ), result2:round( nRoundSize )
- if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then
- return rounded1, rounded2
- else
- return rounded1
- end
- end
- return result:round( nRoundSize )
- end
- local function narrow( p1, p2, fix )
- local dist1 = math.abs( (p1 - fix.vPosition):length() - fix.nDistance )
- local dist2 = math.abs( (p2 - fix.vPosition):length() - fix.nDistance )
- if math.abs(dist1 - dist2) < 0.01 then
- return p1, p2
- elseif dist1 < dist2 then
- return p1:round( nRoundSize )
- else
- return p2:round( nRoundSize )
- end
- end
- local getLocation = function(locations)
- -- PLEASE DON'T KILL ME FOR USING THESE VARIABLE NAMES, IT WAS WRITTEN BY DAN200
- -- Code taken from /rom/apis/gps by dan200
- local fixes = {}
- for k, location in pairs(locations) do
- table.insert(fixes, {vPosition = vector.new( location.x, location.y, location.z ), nDistance = location.dist})
- end
- local pos1, pos2 = trilaterate(fixes[1], fixes[2], fixes[3])
- if #fixes > 3 then
- for i = 4, #fixes do
- pos1, pos2 = narrow(pos1, pos2, fixes[i])
- end
- end
- if pos1 and pos2 then
- return "Possible locations: x: ".. pos1.x.." y: ".. pos1.y.." z: "..pos1.z, "or x: "..pos2.x.." y: "..pos2.y.." z: "..pos2.z
- elseif pos1 then
- return "Position: x: ".. pos1.x.." y: ".. pos1.y.." z: "..pos1.z
- else
- return "Unknown"
- end
- end
- local isInMessageList = function(id)
- for k,v in pairs(messageList) do
- if v.id == id then
- return true
- end
- end
- return false
- end
- local ddosMonitor = function()
- server.modem.open(rednet.CHANNEL_REPEAT)
- while true do
- local event, side, channel, reply, message, dist = os.pullEventRaw()
- if event == "modem_message" and channel == rednet.CHANNEL_REPEAT and type(message) == "table" then
- if message.nMessageID and message.nRecipient and not isInMessageList(message.nMessageID) then
- table.insert(messageList, {origin = reply, target = message.nRecipient, id = message.nMessageID, expiry = os.clock() + expiry})
- if #messageList > 100 then
- table.remove(messageList, 1)
- end
- end
- elseif event == "timer" and side == checkTimer then
- findSpammers()
- checkTimer = os.startTimer(1)
- end
- end
- end
- local profileConstructor = function()
- while true do
- os.pullEvent()
- while #profileQueue > 0 do
- local currentProfile = profileQueue[1]
- table.remove(profileQueue, 1)
- local target = ""
- if tonumber(currentProfile.target) == rednet.CHANNEL_BROADCAST then
- target = "Broadcast"
- else
- target = currentProfile.target
- end
- if locationDatabase[currentProfile.origin] and (locationDatabase[currentProfile.origin].expiry > os.clock()) then
- cachedLocation = locationDatabase[currentProfile.origin]
- activeSpam[currentProfile.origin] = {active = os.clock(), locationA = cachedLocation.locationA, locationB = cachedLocation.locationB, target = target}
- else
- local responses = {}
- server.modem.open(operatingChannel)
- server.modem.transmit(operatingChannel, {
- ddosMonitor = "request",
- messageID = currentProfile.id
- })
- local timeout = os.startTimer(2)
- while true do
- local event, side, channel, reply, message = os.pullEventRaw()
- if event == "modem_message" and type(message) == "table" and message.ddosMonitor and message.ddosMonitor == "response" then
- if message.x and message.y and message.z and message.dist and message.id and message.id == currentProfile.id then
- table.insert(responses, {x = message.x, y = message.y, z = message.z, dist = message.dist})
- end
- elseif event == "timer" and side == timeout then
- if #responses > 2 then
- locationA, locationB = getLocation(responses)
- locationDatabase[currentProfile.origin] = {locationA = locationA, locationB = locationB, expiry = os.clock() + freshness}
- activeSpam[currentProfile.origin] = {active = os.clock(), locationA = locationA, locationB = locationB, target = target}
- else
- activeSpam[currentProfile.origin] = {active = os.clock(), locationA = "Unknown", target = target}
- end
- break
- end
- end
- end
- end
- end
- end
- local main = function()
- while true do
- parallel.waitForAny(ddosMonitor, profileConstructor)
- end
- end
- server.runCoroutine(main)
- local getData = function()
- local returnTable = {}
- local spamCopy = {}
- for k,v in pairs(activeSpam) do
- spamCopy[k] = v
- end
- for i = 1, 3 do
- local mostRecent = math.huge
- local mostRecentID = nil
- for id, v in pairs(spamCopy) do
- if ((os.clock() - v.active) < expired) and (os.clock() - v.active) < mostRecent then
- mostRecent = (os.clock() - v.active)
- mostRecentID = id
- end
- end
- if mostRecentID then
- table.insert(returnTable, {id = mostRecentID, time = mostRecent,
- locationA = spamCopy[mostRecentID].locationA, locationB = spamCopy[mostRecentID].locationB})
- spamCopy[mostRecentID] = nil
- end
- end
- local a = textutils.serialize(returnTable)
- return a
- end
- server.handleRequest("get", getData)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement