EVOLUTION-MANAGER
Edit File: httpSearch.lua
package.path = "./lua/?.lua;/usr/local/lib/nprobe/lua/?.lua;" .. package.path local j = require("dkjson") -- ################################################## -- ################################################## -- A Lua implementation of the Aho-Corasick string matching algorithm -- -- Copyright (c) 2013-2014 CloudFlare, Inc. -- -- Usage: -- -- local AC = require 'aho-corasick' -- -- t = AC.build({'words', 'to', 'find'}) -- r = AC.match(t, 'try to find in this string') -- r == {'to', 'find'} local M = {} local byte = string.byte local char = string.char local debug = false local root = "" -- make: creates a new entry in t for the given string c with optional fail -- state local function make(t, c, f) t[c] = {} t[c].to = {} t[c].fail = f t[c].hit = root t[c].word = false end -- build: builds the Aho-Corasick data structure from an array of strings function ac_build(m) local t = {} make(t, root, root) for i = 1, #m do local current = root -- Build the tos which capture the transitions within the tree for j = 1, m[i]:len() do local c = byte(m[i], j) local path = current .. char(c) if t[current].to[c] == nil then t[current].to[c] = path if current == root then make(t, path, root) else make(t, path) end end current = path end t[m[i]].word = true end -- Build the fails which show how to backtrack when a fail matches and -- build the hits which connect nodes to suffixes that are words local q = {root} while #q > 0 do local path = table.remove(q, 1) for c, p in pairs(t[path].to) do table.insert(q, p) local fail = p:sub(2) while fail ~= "" and t[fail] == nil do fail = fail:sub(2) end if fail == "" then fail = root end t[p].fail = fail local hit = p:sub(2) while hit ~= "" and (t[hit] == nil or not t[hit].word) do hit = hit:sub(2) end if hit == "" then hit = root end t[p].hit = hit end end return t end -- match: checks to see if the passed in string matches the passed in tree -- created with build. If all is true (the default) an array of all matches is -- returned. If all is false then only the first match is returned. function ac_match(t, s, all) if all == nil then all = true end local path = root local hits = nil local hits_idx = 0 for i = 1,s:len() do local c = byte(s, i) while t[path].to[c] == nil and path ~= root do path = t[path].fail end local n = t[path].to[c] if n ~= nil then path = n if t[n].word then if(hits_idx == 0) then hits = { } end hits_idx = hits_idx + 1 hits[hits_idx] = n end while t[n].hit ~= root do if(hits_idx == 0) then hits = { } end n = t[n].hit hits_idx = hits_idx + 1 hits[hits_idx] = n end if all == false and hits_idx > 0 then return hits end end end return hits end -- ################################################## -- ################################################## function tprint (tbl, indent) if not indent then indent = 0 end for k, v in pairs(tbl) do formatting = string.rep(" ", indent) .. k .. ": " if type(v) == "table" then print(formatting) tprint(v, indent+1) elseif type(v) == 'boolean' then print(formatting .. tostring(v)) else print(formatting .. v) end end end -- ################################################## function stringContainsIgnoreCase(str, tofind) if(str == nil or tofind == nil) then return false end return (string.find(string.lower(str), string.lower(tofind), 1) ~= nil) end -- ################################################## -- methods to accept local methods = { } -- matching return codes to filter in local retcode = { } -- matching return content-type to filter in local contenttypes = { } -- matching URLs to filter out (blacklist) local urls = { } local url = ac_build(urls) -- ============================================================= -- ============================================================= function refreshHTTPmethods() local rule = redis.hget("nprobe.rules", "http.methods") if((rule == "") or (rule == nil)) then rule = "[ ]" else rule = string.gsub(rule, "\n", "") end local obj, pos, err = j.decode(rule, 1, nil) methods = { } if err then print ("Error:", err) else for i = 1,#obj do methods[obj[i]] = true end end end -- ========================== function refreshHTTPretcode() local rule = redis.hget("nprobe.rules", "http.retcode") if((rule == "") or (rule == nil)) then rule = "[ ]" else rule = string.gsub(rule, "\n", "") end local obj, pos, err = j.decode(rule, 1, nil) retcode = { } if err then print ("Error:", err) else for i = 1,#obj do retcode[obj[i]] = true end end end -- ========================== function refreshHTTPcontenttypes() local rule = redis.hget("nprobe.rules", "http.contenttypes") if((rule == "") or (rule == nil)) then rule = "[ ]" else rule = string.gsub(rule, "\n", "") end local obj, pos, err = j.decode(rule, 1, nil) contenttypes = { } if err then print ("Error:", err) else for i = 1,#obj do contenttypes[obj[i]] = true end end end -- ========================== function refreshHTTPurl() local rule = redis.hget("nprobe.rules", "http.urls") if((rule == "") or (rule == nil)) then rule = "[ ]" else rule = string.gsub(rule, "\n", "") end local obj, pos, err = j.decode(rule, 1, nil) urls = { } if err then print ("Error:", err) else for i = 1,#obj do table.insert(urls, obj[i]) end end url = ac_build(urls) end -- ========================== function refreshRules(when) refreshHTTPmethods() refreshHTTPretcode() refreshHTTPcontenttypes() refreshHTTPurl() if(debug) then dumpTableKeys("[nprobe.rules] methods", methods) dumpTableKeys("[nprobe.rules] retcode", retcode) dumpTableKeys("[nprobe.rules] contenttypes", contenttypes) dumpTableValues("[nprobe.rules] urls", urls) end end function updateRules() if rules.haveChanged() then refreshRules(os.time()) rules.updated() end end -- ============================================================= -- ============================================================= -- List of available HTTP info: -- http.client -- http.server -- http.url -- http.location -- http.retcode -- http.useragent -- http.contenttype -- http.cookie -- http.applicationid -- http.application -- http.flowusername -- http.proto -- http.serverip -- http.postparams function checkHTTPFlow() updateRules() --print("[LUA] ************************\n") if(debug) then tprint(http) end dropFlow = false -- print(http["http.method"].."\n") if(not methods[http["http.method"]] == true) then if(debug) then print(string.format("[LUA] DROP (Method filtered out) [method: %s]\n", http["http.method"])) end dropFlow = true return end if(http["http.url"] == nil) then if(debug) then print(string.format("[LUA] no http data (not http?) [url: %s]\n", http["http.url"])) end dropFlow = true return end if(not retcode[http["http.retcode"]] == true) then if(debug) then print(string.format("[LUA] DROP (RetCode filtered out) [retcode: %s]\n", http["http.retcode"])) end dropFlow = true return end if(http["http.contenttype"] ~= nil) then if(not contenttypes[string.lower(http["http.contenttype"])] == true) then if(debug) then print(string.format("[LUA] DROP (ContentType filtered out) [contenttype: %s]\n", http["http.contenttype"])) end dropFlow = true return end else if(debug) then print("[LUA] DROP (no ContentType)\n") end dropFlow = true return end local hits = ac_match(url, http["http.url"]) if(hits ~= nil) then if(debug) then print(string.format("[LUA] DROP (URL match) [url: %s]\n", http["http.url"])) end dropFlow = true return end if(debug) then print("---------------------------------------\n") if(dropFlow == false) then print("PASS\n") else print("DROP\n") end print("---------------------------------------\n") end end function dumpTable(label, what) for k,v in pairs(what) do print(label.."\t"..k.."="..v.."\n") end end function dumpTableKeys(label, what) for k,v in pairs(what) do print(label.."\t"..k.."\n") end end function dumpTableValues(label, what) for k,v in pairs(what) do print(label.."\t"..v.."\n") end end -- ################################################## print("[LUA] Welcome to nProbe Lua!\n") refreshRules(os.time())