From: Russ Combs Date: Tue, 18 Aug 2015 14:56:52 +0000 (-0400) Subject: Squashed commit of the following: X-Git-Tag: 3.0.0-233~866 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f50fc93eb47e7efe4b1dc3f4b062e72fac6a87c6;p=thirdparty%2Fsnort3.git Squashed commit of the following: commit 1a1ce657531761060cc32a96106796ddcf2e9d44 Author: Joel Cornett Date: Mon Aug 17 17:53:20 2015 -0400 added lua/dev_notes.txt Improved script-path & added IpApi Lua object, Enable multiple script-path args and let script-path specify single files Improved/refactored common.lua script Added IpApi Lua object --- diff --git a/piglet/tests/common.lua b/piglet/tests/common.lua new file mode 100644 index 000000000..9eba64efc --- /dev/null +++ b/piglet/tests/common.lua @@ -0,0 +1,215 @@ +do + local table = table + local meta = { __index = table} + + -- give tables metatable access to the table library + function table.new(t) + t = t or { } + setmetatable(t, meta) + return t + end + + function table:imap(fn) + local iter, a, s = ipairs(self) + local closure = function(...) + local i, v = iter(...) + return i, fn(v) + end + + return closure, self, 0 + end + + function table:ifilter(fn) + local iter, a, s = ipairs(self) + local closure = function(...) + local i, v = iter(...) + while i ~= nil and not fn(v) do + i, v = iter(a, i) + end + + return i, v + end + + return closure, self, 0 + end + + function table:vomit(depth, seen, out) + depth = depth or 0 + + -- maintain a list of dumped tables to + -- avoid infinite loops + seen = seen or { } + out = out or { } + + local indent = strint.rep(" ", level) + + for n, v in pairs(self) do + if type(v) == "table" and not seen[v] then + seen[v] = true + table.insert(out, string.format("%s%s =", indent, tostring(n))) + self.vomit(v, depth + 1, seen, out) + else + table.insert(out, string.format("%s%s = %s", indent, tostring(n), tostring(v))) + end + end + + return table.concat(out, "\n") + end + + function meta:__tostring() + if DEBUG then + return self:vomit() + else + return table.__tostring(self) + end + end +end + +-- string library extensions +do + function string:encode_hex() + local out = table.new() + + for tok in self:gmatch("%f[%x](%x%x)") do + if tok:gmatch("^%x+$") then + out:insert(string.char(tonumber(tok, 16))) + end + end + + return out:concat("") + end + + function string:decode_printable() + local out = table.new() + + for tok in self:gmatch(".") do + if tok:match("%g") then + out:insert(tok) + else + out:insert(".") + end + end + + return out:concat("") + end + + function string:decode_hex() + local out = table.new() + + for tok in self:gmatch(".") do + out:insert(string.format("%02x", string.byte(tok))) + end + + return out:concat(" ") + end +end + +-- Assertions library +check = { } +do + function raise(title, msg, lvl) + lvl = lvl or 3 + local info = debug.getinfo(lvl) + error( + string.format( + "%s:%d: %s: %s", + info.short_src, + info.currentline, + title, + msg + ) + ) + end + + function check.tables_equal(exp, act) + if exp == act then return end + + for n, e in pairs(exp) do + local a = act[n] + if a ~= e then + raise( + "tables unequal", + string.format( + "item with key %s differs (%s ~= %s)", + tostring(n), + tostring(e), + tostring(a) + ) + ) + end + end + end + + function check.arrays_equal(exp, act) + if exp == act then return end + if #exp ~= #act then + raise( + "arrays unequal", + string.format( + "lengths differ (#%d ~= #%d)", + #exp, #act + ) + ) + end + + for i, e in ipairs(exp) do + local a = act[i] + if e ~= a then + raise( + "arrays unequal", + string.format( + "item at index %d differs (%s ~= %s)", + i, tostring(e), tostring(a) + ) + ) + end + end + end + + function check.raises(fn, msg) + local ok, err = pcall(fn) + if ok then + raise("did not throw", msg or "") + end + end + + function check.check(expr, msg) + if not expr then + raise("assertion failed", msg or "") + end + end +end + +-- Test runner +function run_tests(tests) + local failed = false + + for name, fn in pairs(tests) do + ok, err = pcall(fn) + if not ok then + print("--", name, err) + failed = true + end + end + + return not failed +end + +-- Misc utils +packet = { } +do + function packet.construct_ip4(hdr, data) + local rb = RawBuffer.new(hdr .. data) + local dd = DecodeData.new() + local p = Packet.new(rb) + + local ip_api = dd:get_ip_api() + ip_api:set_ip4(rb) + + p:set_data(#hdr, #data) + p:set { proto_bits = 4 } + p:set_decode_data(dd) + + return p + end +end diff --git a/piglet/tests/instance/codec.lua b/piglet/tests/instance/codec.lua index d33d5a309..aa0ce406e 100644 --- a/piglet/tests/instance/codec.lua +++ b/piglet/tests/instance/codec.lua @@ -3,8 +3,8 @@ plugin = type = "piglet", name = "codec::ipv4", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -19,12 +19,12 @@ tests = get_data_link_type = function() local rv = Codec.get_data_link_type() - assert_list_eq("data_link_types", DATA_LINK_TYPES, rv) + check.arrays_equal(DATA_LINK_TYPES, rv) end, get_protocol_ids = function() local rv = Codec.get_protocol_ids() - assert_list_eq("data_link_types", PROTOCOL_IDS, rv) + check.arrays_equal(PROTOCOL_IDS, rv) end, decode = function() diff --git a/piglet/tests/instance/common.lua b/piglet/tests/instance/common.lua deleted file mode 100644 index 28bb7244c..000000000 --- a/piglet/tests/instance/common.lua +++ /dev/null @@ -1,92 +0,0 @@ -run_all = function(tests) - local failed = false - - for name, fn in pairs(tests) do - ok, msg = pcall(fn) - if not ok then - print("--", name, msg) - failed = true - end - end - - if failed then - return false - end - - return true -end - -assert_table_eq = function(name, expected, actual) - for n, exp in pairs(expected) do - local a = actual[n] - assert( - exp == a, - name .. "." .. n .. ": " .. tostring(exp) .. " != " .. tostring(a) - ) - end -end - -assert_list_eq = function(name, expected, actual) - assert(#expected == #actual, "sizes differ") - for i, exp in ipairs(expected) do - local a = actual[i] - assert( - exp == a, - name .. "[" .. tostring(i) .. "]" .. " != " .. tostring(a) - ) - end -end - -assert_err = function(fn, msg) - local e, m = pcall(fn) - assert(not e, "failed to raise an error") - - local m_s = tostring(m) - assert(m_s:match(msg), "error message '" .. tostring(m) .. "' ! '" .. msg .. "'") -end - -get_ipv4_packet = function(hdr, data) - local rb = RawBuffer.new(hdr .. data) - local dd = DecodeData.new() - local p = Packet.new(rb) - - dd:set_ipv4_hdr(rb) - - p:set_data(#hdr, #data) - p:set({ proto_bits = 4 }) - p:set_decode_data(dd) - - return p, rb -end - -string.as_content_hex = function(str) - local vals = {} - for tok in str:gmatch("(%x+)%s*") do - table.insert(vals, string.char(tonumber(tok, 16))) - end - - return table.concat(vals, "") -end - -string.dump_hex = function(str) - local vals = {} - for tok in str:gmatch(".") do - table.insert(vals, string.format("%x", tok:byte())) - end - - return table.concat(vals, "") -end - -string.dump_human = function(str) - local vals = {} - for tok in str:gmatch(".") do - if tok:match("%g") then - table.insert(vals, tok) - else - table.insert(vals, ".") - end - end - - return table.concat(vals, "") -end - diff --git a/piglet/tests/instance/inspector.lua b/piglet/tests/instance/inspector.lua index cedef29b6..c8ee33bb2 100644 --- a/piglet/tests/instance/inspector.lua +++ b/piglet/tests/instance/inspector.lua @@ -3,14 +3,14 @@ plugin = type = "piglet", name = "inspector::telnet", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end, -- FIXIT-L: Need this to keep Inspector.configure() happy use_defaults = true } -HEADER = [[ +IP4 = [[ 45 | 00 | 00 46 | 00 00 | 00 00 | 01 | 06 00 00 | 00 00 00 01 | 00 00 00 02 @@ -21,7 +21,7 @@ HEADER = [[ DATA = "abcdefghijklmnopqrstuvwxyz" get_packet = function() - return get_ipv4_packet(HEADER:as_content_hex(), DATA) + return packet.construct_ip4(IP4:encode_hex(), DATA) end tests = diff --git a/piglet/tests/instance/ips_action.lua b/piglet/tests/instance/ips_action.lua index 7909f65af..192ba125c 100644 --- a/piglet/tests/instance/ips_action.lua +++ b/piglet/tests/instance/ips_action.lua @@ -3,8 +3,8 @@ plugin = type = "piglet", name = "ips_action::react", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/instance/ips_option.lua b/piglet/tests/instance/ips_option.lua index a48ba5cb3..5b944e668 100644 --- a/piglet/tests/instance/ips_option.lua +++ b/piglet/tests/instance/ips_option.lua @@ -3,8 +3,8 @@ plugin = type = "piglet", name = "ips_option::content", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/instance/logger.lua b/piglet/tests/instance/logger.lua index 58d2e61e2..177f46385 100644 --- a/piglet/tests/instance/logger.lua +++ b/piglet/tests/instance/logger.lua @@ -4,15 +4,16 @@ plugin = name = "logger::alert_csv", use_defaults = true, test = function() + dofile(SCRIPT_DIR .. "/../common.lua") + Logger.open() - dofile(SCRIPT_DIR .. "/common.lua") - local rv = run_all(tests) + local rv = run_tests(tests) Logger.close() return rv end } -HEADER = [[ +IP4 = [[ 45 | 00 | 00 46 | 00 00 | 00 00 | 01 | 06 00 00 | 00 00 00 01 | 00 00 00 02 @@ -22,13 +23,9 @@ HEADER = [[ DATA = "abcdefghijklmnopqrstuvwxyz" -get_packet = function() - return get_ipv4_packet(HEADER:as_content_hex(), DATA) -end - tests = { - initialize = function() + exists = function() assert(Logger) end, @@ -37,17 +34,19 @@ tests = end, alert = function() - local p, rb = get_packet() + local p = packet.construct_ip4(IP4:encode_hex(), DATA) local e = Event.new() - e:set({ generator = 135, id = 2 }) + + e:set { generator = 135, id = 2 } Logger.alert(p, "foo", e) end, log = function() - local p, rb = get_packet() + local p = packet.construct_ip4(IP4:encode_hex(), DATA) local e = Event.new() - e:set({ generator = 135, id = 2 }) + + e:set { generator = 135, id = 2 } Logger.log(p, "foo", e) end diff --git a/piglet/tests/instance/search_engine.lua b/piglet/tests/instance/search_engine.lua index ecc91d82d..c5968fc8c 100644 --- a/piglet/tests/instance/search_engine.lua +++ b/piglet/tests/instance/search_engine.lua @@ -3,8 +3,8 @@ plugin = type = "piglet", name = "search_engine::ac_full", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/instance/so_rule.lua b/piglet/tests/instance/so_rule.lua index 3b2dac457..de1da31cd 100644 --- a/piglet/tests/instance/so_rule.lua +++ b/piglet/tests/instance/so_rule.lua @@ -3,8 +3,8 @@ xplugin = type = "piglet", name = "so_rule::need_rule", test = function() - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/interface/buffer.lua b/piglet/tests/interface/buffer.lua index b58dc4774..00cfa2e99 100644 --- a/piglet/tests/interface/buffer.lua +++ b/piglet/tests/interface/buffer.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::buffer", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/interface/codec_data.lua b/piglet/tests/interface/codec_data.lua index 09cd22362..4afcf40f8 100644 --- a/piglet/tests/interface/codec_data.lua +++ b/piglet/tests/interface/codec_data.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::codec_data", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -52,8 +51,8 @@ tests = initialize_with_table = function() local cd = CodecData.new() - assert_table_eq("get()", DEFAULT_VALUES, cd:get()) + check.tables_equal(DEFAULT_VALUES, cd:get()) cd:set(VALUES) - assert_table_eq("set()", VALUES, cd:get()) + check.tables_equal(VALUES, cd:get()) end } diff --git a/piglet/tests/interface/common.lua b/piglet/tests/interface/common.lua deleted file mode 100644 index 3b89c82eb..000000000 --- a/piglet/tests/interface/common.lua +++ /dev/null @@ -1,78 +0,0 @@ -run_all = function(tests) - local failed = false - - for name, fn in pairs(tests) do - ok, msg = pcall(fn) - if not ok then - print("--", name, msg) - failed = true - end - end - - if failed then - return false - end - - return true -end - -assert_table_eq = function(name, expected, actual) - for n, exp in pairs(expected) do - local a = actual[n] - assert( - exp == a, - name .. "." .. n .. ": " .. tostring(exp) .. " != " .. tostring(a) - ) - end -end - -assert_list_eq = function(name, expected, actual) - assert(#expected == #actual, "sizes differ") - for i, exp in ipairs(expected) do - local a = actual[i] - assert( - exp == a, - name .. "[" .. tostring(i) .. "]" .. " != " .. tostring(a) - ) - end -end - -assert_err = function(fn, msg) - local e, m = pcall(fn) - assert(not e, "failed to raise an error") - - local m_s = tostring(m) - assert(m_s:match(msg), "error message '" .. tostring(m) .. "' ! '" .. msg .. "'") -end - -string.as_content_hex = function(str) - local vals = {} - for tok in str:gmatch("(%x+)%s*") do - table.insert(vals, string.char(tonumber(tok, 16))) - end - - return table.concat(vals, "") -end - -string.dump_hex = function(str) - local vals = {} - for tok in str:gmatch(".") do - table.insert(vals, string.format("%x", tok:byte())) - end - - return table.concat(vals, "") -end - -string.dump_human = function(str) - local vals = {} - for tok in str:gmatch(".") do - if tok:match("%g") then - table.insert(vals, tok) - else - table.insert(vals, ".") - end - end - - return table.concat(vals, "") -end - diff --git a/piglet/tests/interface/cursor.lua b/piglet/tests/interface/cursor.lua index ec978c4f1..2fbd002de 100644 --- a/piglet/tests/interface/cursor.lua +++ b/piglet/tests/interface/cursor.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::cursor", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/interface/daq_header.lua b/piglet/tests/interface/daq_header.lua index fe64d2aed..72b4ab9e5 100644 --- a/piglet/tests/interface/daq_header.lua +++ b/piglet/tests/interface/daq_header.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::daq_header", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -42,11 +41,11 @@ tests = initialize_default = function() local daq = DAQHeader.new() assert(daq) - assert("default", DEFAULT_VALUES, daq:get()) + check.tables_equal(DEFAULT_VALUES, daq:get()) end, initialize_with_table = function() local daq = DAQHeader.new(VALUES) - assert_table_eq("init", VALUES, daq:get()) + check.tables_equal(VALUES, daq:get()) end } diff --git a/piglet/tests/interface/decode_data.lua b/piglet/tests/interface/decode_data.lua index 782603223..fee728fbc 100644 --- a/piglet/tests/interface/decode_data.lua +++ b/piglet/tests/interface/decode_data.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::decode_data", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -25,16 +24,31 @@ VALUES = type = 4 } +IP4 = [[ +45 | 00 | 00 46 | 00 00 | 00 00 | 01 | 06 +00 00 | 00 00 00 01 | 00 00 00 02 + +00 00 | 00 00 | 00 00 00 00 | 00 00 00 00 | 06 02 +00 00 ff ff | 00 00 | 00 00 | 00 00 +]] + tests = { initialize_default = function() local dd = DecodeData.new() assert(dd) - assert("default", DEFAULT_VALUES, dd:get()) + check.tables_equal(DEFAULT_VALUES, dd:get()) end, initialize_with_table = function() local dd = DecodeData.new(VALUES) - assert_table_eq("init", VALUES, dd:get()) + check.tables_equal(VALUES, dd:get()) + end, + + ip_api = function() + local dd = DecodeData.new(VALUES) + local ip = dd:get_ip_api() + local raw = IP4:encode_hex() + ip:set_ip4(raw) end } diff --git a/piglet/tests/interface/enc_state.lua b/piglet/tests/interface/enc_state.lua index 54d7ef240..5f6ff2d3b 100644 --- a/piglet/tests/interface/enc_state.lua +++ b/piglet/tests/interface/enc_state.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::enc_state", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/interface/event.lua b/piglet/tests/interface/event.lua index a04f226f0..d2acdd0ac 100644 --- a/piglet/tests/interface/event.lua +++ b/piglet/tests/interface/event.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::event", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -52,18 +51,18 @@ tests = init_with_table = function() local event = Event.new(VALUES) - assert_table_eq("get()", VALUES, event:get()) + check.tables_equal(VALUES, event:get()) end, get_and_set = function() local event = Event.new() - assert_table_eq("get()", DEFAULT_VALUES, event:get()) - assert_table_eq("get().sig_info", DEFAULT_SIGINFO_VALUES, event:get().sig_info) + check.tables_equal(DEFAULT_VALUES, event:get()) + check.tables_equal(DEFAULT_SIGINFO_VALUES, event:get().sig_info) event:set(VALUES) event:set({ sig_info = SIGINFO_VALUES }) - assert_table_eq("set()", VALUES, event:get()) - assert_table_eq("get().sig_info", SIGINFO_VALUES, event:get().sig_info) + check.tables_equal(VALUES, event:get()) + check.tables_equal(SIGINFO_VALUES, event:get().sig_info) end } diff --git a/piglet/tests/interface/flow.lua b/piglet/tests/interface/flow.lua index 3ba987c95..7eacc4ba3 100644 --- a/piglet/tests/interface/flow.lua +++ b/piglet/tests/interface/flow.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::flow", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } diff --git a/piglet/tests/interface/packet.lua b/piglet/tests/interface/packet.lua index 350a7ba6c..ff0f62a87 100644 --- a/piglet/tests/interface/packet.lua +++ b/piglet/tests/interface/packet.lua @@ -3,9 +3,8 @@ plugin = type = "piglet", name = "piglet::packet", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } @@ -66,7 +65,7 @@ tests = init_with_table = function() local p = Packet.new(VALUES) - assert_table_eq("get()", VALUES, p:get()) + check.tables_equal(VALUES, p:get()) end, init_with_everything = function() @@ -94,8 +93,8 @@ tests = get_and_set = function() local p = Packet.new() - assert_table_eq("get()", DEFAULT_VALUES, p:get()) + check.tables_equal(DEFAULT_VALUES, p:get()) p:set(VALUES) - assert_table_eq("set()", VALUES, p:get()) + check.tables_equal(VALUES, p:get()) end } diff --git a/piglet/tests/interface/raw_buffer.lua b/piglet/tests/interface/raw_buffer.lua index 7cb5fe6b4..aada788f6 100644 --- a/piglet/tests/interface/raw_buffer.lua +++ b/piglet/tests/interface/raw_buffer.lua @@ -3,15 +3,14 @@ plugin = type = "piglet", name = "piglet::raw_buffer", test = function() - -- Put the dofile here so that it doesn't get loaded twice - dofile(SCRIPT_DIR .. "/common.lua") - return run_all(tests) + dofile(SCRIPT_DIR .. "/../common.lua") + return run_tests(tests) end } INIT_SIZE = 16 INIT_STRING = "foobar" -INIT_16_CONTENT = "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0" +INIT_16_CONTENT = string.rep("00 ", 16) tests = { @@ -24,7 +23,7 @@ tests = initialize_with_size = function() local rb = RawBuffer.new(INIT_SIZE) assert(rb:size() == INIT_SIZE) - assert(rb:read() == INIT_16_CONTENT:as_content_hex()) + assert(rb:read() == INIT_16_CONTENT:encode_hex()) end, initialize_with_string = function() @@ -74,16 +73,16 @@ tests = assert(#rv == 0, "length should equal 0, not " .. tostring(rv)) -- read oor with 1 arg (-1, 10) - assert_err(function() rb:read(-1) end, "bad argument") - assert_err(function() rb:read(2) end, "bad argument") + check.raises(function() rb:read(-1) end) + check.raises(function() rb:read(2) end) -- read with 2 args rv = rb:read(0, 0) assert(#rv == 0, "length should equal 0, not " .. tostring(rv)) -- read oor with 2 args - assert_err(function() rb:read(-1, 0) end, "bad argument") - assert_err(function() rb:read(0, 2) end, "bad argument") + check.raises(function() rb:read(-1, 0) end) + check.raises(function() rb:read(0, 2) end) end, read_nonempty = function() @@ -102,7 +101,7 @@ tests = assert(rv == "fo") -- read oob with 1 arg - assert_err(function() rb:read(10) end, "bad argument") + check.raises(function() rb:read(10) end) -- read with 2 args (full string), offset, length rv = rb:read(0, rb:size()) @@ -117,8 +116,8 @@ tests = assert(rv == "ooba") -- read oob with 2 args (offset/length) - assert_err(function() rb:read(-1, rb:size()) end, "bad argument") - assert_err(function() rb:read(0, rb:size() + 1) end, "bad argument") + check.raises(function() rb:read(-1, rb:size()) end) + check.raises(function() rb:read(0, rb:size() + 1) end) end, resize = function() diff --git a/src/lua/dev_notes.txt b/src/lua/dev_notes.txt new file mode 100644 index 000000000..1e276816d --- /dev/null +++ b/src/lua/dev_notes.txt @@ -0,0 +1,12 @@ +Templates and helpers for interfacing with luajit virtual machines are here: + +* *State* is a RAII wrapper to the lua_State* pointer +* *ManageStack* prevents the stack from growing too large and ensures that + the stack has enough room. +* *Args* provides type checking access to the lua stack from within an + embedded C/C\++ function +* *lua_iface.h* contains wrappers for exposing C\++ objects to lua +* *lua_ref.h* provides a very basic reference tracking system to prevent + Lua from garbage collecting objects that may be in use but are otherwise + untracked in the lua vm. +* *lua_stack.h* provides templated functions to interface with the stack diff --git a/src/main/help.cc b/src/main/help.cc index 8613d8262..6955dc850 100644 --- a/src/main/help.cc +++ b/src/main/help.cc @@ -155,7 +155,7 @@ enum HelpType static void show_help(SnortConfig* sc, const char* val, HelpType ht) { snort_conf = new SnortConfig; - ScriptManager::load_scripts(sc->script_path); + ScriptManager::load_scripts(sc->script_paths); PluginManager::load_plugins(sc->plugin_path); ModuleManager::init(); diff --git a/src/main/snort.cc b/src/main/snort.cc index 95a342bb1..ee87fa6a4 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -237,7 +237,7 @@ void Snort::init(int argc, char** argv) #endif ModuleManager::init(); - ScriptManager::load_scripts(snort_cmd_line_conf->script_path); + ScriptManager::load_scripts(snort_cmd_line_conf->script_paths); PluginManager::load_plugins(snort_cmd_line_conf->plugin_path); if ( snort_conf->logging_flags & LOGGING_FLAG__SHOW_PLUGINS ) diff --git a/src/main/snort_config.h b/src/main/snort_config.h index b8d98ce54..8748b23df 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -193,7 +193,7 @@ public: std::string chroot_dir; /* -t or config chroot */ std::string plugin_path; - std::string script_path; + std::vector script_paths; mode_t file_mask = 0; diff --git a/src/main/snort_module.cc b/src/main/snort_module.cc index 030bea783..f321c1327 100644 --- a/src/main/snort_module.cc +++ b/src/main/snort_module.cc @@ -410,7 +410,7 @@ static const Parameter s_params[] = " prepend this to each output file" }, { "--script-path", Parameter::PT_STRING, nullptr, nullptr, - " where to find luajit scripts" }, + " to a luajit script or directory containing luajit scripts" }, #ifdef BUILD_SHELL { "--shell", Parameter::PT_IMPLIED, nullptr, nullptr, @@ -801,7 +801,7 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) sc->run_prefix = v.get_string(); else if ( v.is("--script-path") ) - ConfigScriptPath(sc, v.get_string()); + ConfigScriptPaths(sc, v.get_string()); #ifdef BUILD_SHELL else if ( v.is("--shell") ) diff --git a/src/managers/script_manager.cc b/src/managers/script_manager.cc index b7a221655..0920b510d 100644 --- a/src/managers/script_manager.cc +++ b/src/managers/script_manager.cc @@ -23,6 +23,7 @@ #include #include +#include #include #include "ips_manager.h" @@ -241,26 +242,43 @@ static void load_script(const char* f) // public methods //------------------------------------------------------------------------- -void ScriptManager::load_scripts(const std::string& paths) +void ScriptManager::load_scripts(const std::vector& paths) { + struct stat s; + if ( paths.empty() ) return; - const char* t = paths.c_str(); - vector buf(t, t+strlen(t)+1); - char* last, * s; + for ( auto path : paths ) + { + size_t pos = path.find_first_not_of(":"); - s = strtok_r(&buf[0], ":", &last); + while ( pos != std::string::npos ) + { + size_t end_pos = path.find_first_of(":", pos); - while ( s ) - { - Directory d(s); - const char* f; + std::string d = path.substr( + pos, (end_pos == std::string::npos) ? end_pos : end_pos - pos); + + pos = ( end_pos == std::string::npos ) ? end_pos : end_pos + 1; + + if ( d.empty() ) + continue; + + if ( stat(d.c_str(), &s) ) + continue; - while ( (f = d.next(script_ext)) ) - load_script(f); + if ( s.st_mode & S_IFDIR ) + { + Directory dir(d.c_str()); + const char* f; + while ( (f = dir.next(script_ext)) ) + load_script(f); + } - s = strtok_r(nullptr, ":", &last); + else + load_script(d.c_str()); + } } } diff --git a/src/managers/script_manager.h b/src/managers/script_manager.h index ec0c499dd..11127774e 100644 --- a/src/managers/script_manager.h +++ b/src/managers/script_manager.h @@ -24,6 +24,7 @@ // in Lua. Runtime use is via the actual plugin type manager. #include +#include #include "main/snort_types.h" #include "framework/base_api.h" @@ -33,7 +34,7 @@ class ScriptManager { public: - static void load_scripts(const std::string& paths); + static void load_scripts(const std::vector& paths); static void release_scripts(); static const BaseApi** get_plugins(); static std::string* get_chunk(const char* key); diff --git a/src/parser/config_file.cc b/src/parser/config_file.cc index 632d14ef6..9ad3a60c5 100644 --- a/src/parser/config_file.cc +++ b/src/parser/config_file.cc @@ -571,10 +571,10 @@ void ConfigPluginPath(SnortConfig* sc, const char* args) sc->plugin_path = args; } -void ConfigScriptPath(SnortConfig* sc, const char* args) +void ConfigScriptPaths(SnortConfig* sc, const char* args) { if ( sc && args ) - sc->script_path = args; + sc->script_paths.push_back(args); } void config_syslog(SnortConfig* sc, const char*) diff --git a/src/parser/config_file.h b/src/parser/config_file.h index 6fa512e1d..9d7380665 100644 --- a/src/parser/config_file.h +++ b/src/parser/config_file.h @@ -54,7 +54,7 @@ void ConfigProcessAllEvents(SnortConfig*, const char*); void ConfigUtc(SnortConfig*, const char*); void ConfigVerbose(SnortConfig*, const char*); void ConfigPluginPath(SnortConfig*, const char*); -void ConfigScriptPath(SnortConfig*, const char*); +void ConfigScriptPaths(SnortConfig*, const char*); void ConfigDstMac(SnortConfig*, const char*); void ConfigSetGid(SnortConfig*, const char*); diff --git a/src/piglet_plugins/CMakeLists.txt b/src/piglet_plugins/CMakeLists.txt index 51cf2375f..c39b27cd6 100644 --- a/src/piglet_plugins/CMakeLists.txt +++ b/src/piglet_plugins/CMakeLists.txt @@ -74,6 +74,7 @@ set ( pp_decode_data_iface.cc pp_flow_iface.cc pp_event_iface.cc + pp_ip_api_iface.cc ) set ( diff --git a/src/piglet_plugins/Makefile.am b/src/piglet_plugins/Makefile.am index e2653f2b1..d68f525d7 100644 --- a/src/piglet_plugins/Makefile.am +++ b/src/piglet_plugins/Makefile.am @@ -24,7 +24,8 @@ pp_packet_iface.cc \ pp_decode_data_iface.cc \ pp_flow_iface.cc \ pp_event_iface.cc \ -pp_daq_pkthdr_iface.cc +pp_daq_pkthdr_iface.cc \ +pp_ip_api_iface.cc plugin_list = \ pp_codec.cc \ diff --git a/src/piglet_plugins/pp_codec_iface.cc b/src/piglet_plugins/pp_codec_iface.cc index af91e8629..718394bab 100644 --- a/src/piglet_plugins/pp_codec_iface.cc +++ b/src/piglet_plugins/pp_codec_iface.cc @@ -35,10 +35,11 @@ #include "pp_daq_pkthdr_iface.h" #include "pp_decode_data_iface.h" #include "pp_enc_state_iface.h" +#include "pp_ip_api_iface.h" #include "pp_raw_buffer_iface.h" -// FIXIT-M: This should be its own object -static const ip::IpApi ip_api {}; +// FIXIT-M delete this, and make the IpApi arg in codec.update required +static const ip::IpApi default_ip_api {}; struct TextLogWrapper { @@ -164,12 +165,24 @@ static const luaL_Reg methods[] = { Lua::Args args(L); - uint32_t flags_hi = args[1].check_size(); - uint32_t flags_lo = args[2].check_size(); - auto& rb = RawBufferIface.get(L, 3); + // FIXIT-M this hacky arg offset stuff is for backwards compatibilty + // it will be removed in later updates + + int off = 0; + const auto* ip_api = &default_ip_api; + + if ( IpApiIface.is(L, 1) ) + { + ip_api = &IpApiIface.get(L, 1); + off++; + } + + uint32_t flags_hi = args[off + 1].check_size(); + uint32_t flags_lo = args[off + 2].check_size(); + auto& rb = RawBufferIface.get(L, off + 3); // FIXIT-L: Args vs Iface is not orthogonal - uint16_t lyr_len = args[4].opt_size(0, rb.size()); + uint16_t lyr_len = args[off + 4].opt_size(0, rb.size()); auto& self = CodecIface.get(L); @@ -177,7 +190,7 @@ static const luaL_Reg methods[] = uint64_t flags = (static_cast(flags_hi) << 8) | flags_lo; - self.update(ip_api, flags, get_mutable_data(rb), lyr_len, + self.update(*ip_api, flags, get_mutable_data(rb), lyr_len, updated_len); lua_pushinteger(L, updated_len); diff --git a/src/piglet_plugins/pp_decode_data_iface.cc b/src/piglet_plugins/pp_decode_data_iface.cc index 9392191a7..edde23848 100644 --- a/src/piglet_plugins/pp_decode_data_iface.cc +++ b/src/piglet_plugins/pp_decode_data_iface.cc @@ -26,10 +26,8 @@ #include "lua/lua_arg.h" #include "lua/lua_table.h" #include "protocols/ipv4.h" -// #include "protocols/tcp.h" -// #include "protocols/udp.h" -// #include "protocols/icmp4.h" +#include "pp_ip_api_iface.h" #include "pp_raw_buffer_iface.h" // FIXIT-H: Add Internet Header objects @@ -91,28 +89,22 @@ static const luaL_Reg methods[] = [](lua_State* L) { return DecodeDataIface.default_getter(L, get_fields); } }, - // FIXIT-L: Need a more sophisticated interface to decode data ip_api { - "set_ipv4_hdr", + // Return a reference to the IpApi attached to DecodeData + "get_ip_api", [](lua_State* L) { Lua::Args args(L); auto& self = DecodeDataIface.get(L, 1); - auto& rb = RawBufferIface.get(L, 2); - size_t offset = args[3].opt_size(0, rb.size()); - // Need enough room for an IPv4 header - if ( (rb.size() - offset) < sizeof(IP4Hdr) ) - luaL_error( - L, "need %d bytes, got %d", - sizeof(IP4Hdr), rb.size() - ); + auto** ip_api = IpApiIface.allocate(L); + *ip_api = &self.ip_api; - self.ip_api.set( - reinterpret_cast(rb.data() + offset)); + // Make sure the decode data doesn't run out from under the ref + Lua::add_ref(L, *ip_api, "decode_data", lua_gettop(L)); - return 0; + return 1; } }, // FIXIT-L: add access to mplsHdr field diff --git a/src/piglet_plugins/pp_inspector.cc b/src/piglet_plugins/pp_inspector.cc index 95fd08712..df9a4e71f 100644 --- a/src/piglet_plugins/pp_inspector.cc +++ b/src/piglet_plugins/pp_inspector.cc @@ -30,6 +30,7 @@ #include "pp_decode_data_iface.h" #include "pp_flow_iface.h" +#include "pp_ip_api_iface.h" #include "pp_packet_iface.h" #include "pp_raw_buffer_iface.h" #include "pp_stream_splitter_iface.h" @@ -72,6 +73,7 @@ bool InspectorPiglet::setup() install(L, DecodeDataIface); install(L, RawBufferIface); install(L, FlowIface); + install(L, IpApiIface); install(L, PacketIface); install(L, StreamSplitterIface); diff --git a/src/piglet_plugins/pp_ip_api_iface.cc b/src/piglet_plugins/pp_ip_api_iface.cc new file mode 100644 index 000000000..4b4079aea --- /dev/null +++ b/src/piglet_plugins/pp_ip_api_iface.cc @@ -0,0 +1,119 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// pp_ip_api_iface.cc author Joel Cornett + +#include "pp_ip_api_iface.h" + +#include +#include + +#include "lua/lua_arg.h" +#include "protocols/ip.h" +#include "protocols/ipv4.h" +#include "protocols/ipv6.h" + +#include "pp_raw_buffer_iface.h" + +template +static void set_header(lua_State* L, ip::IpApi& ip_api, RawBuffer& rb) +{ + if ( rb.size() < sizeof(Header) ) + luaL_error(L, + "buffer is to small to be cast to header, (need %d, got %d)", + sizeof(Header), rb.size() + ); + + else + { + const auto* hdr = reinterpret_cast(rb.data()); + ip_api.set(hdr); + } +} + +template +static int set(lua_State* L) +{ + Lua::Args args(L); + auto & self = IpApiIface.get(L, 1); + + RawBuffer* rb; + int ref_index = 2; + + if ( RawBufferIface.is(L, 2) ) + rb = &RawBufferIface.get(L, 2); + + else + { + size_t len = 0; + const char* data = args[2].check_string(len); + rb = &RawBufferIface.create(L, data, len); + ref_index = lua_gettop(L); + } + + set_header
(L, self, *rb); + + Lua::add_ref(L, &self, "iph", ref_index); + + return 0; +} + +static const luaL_Reg methods[] = +{ + { + "set_ip4", + [](lua_State* L) + { return set(L); } + }, + { + "set_ip6", + [](lua_State* L) + { return set(L); } + }, + { + "reset", + [](lua_State* L) + { IpApiIface.get(L).reset(); return 0; } + }, + { nullptr, nullptr } +}; + +static const luaL_Reg metamethods[] = +{ + { + "__tostring", + [](lua_State* L) + { return IpApiIface.default_tostring(L); } + }, + { + "__gc", + [](lua_State* L) + { + auto& self = IpApiIface.get(L); + Lua::remove_refs(L, static_cast(&self)); + return 0; + } + }, + { nullptr, nullptr } +}; + +const struct Lua::TypeInterface IpApiIface = +{ + "IpApi", + methods, + metamethods +}; diff --git a/src/piglet_plugins/pp_ip_api_iface.h b/src/piglet_plugins/pp_ip_api_iface.h new file mode 100644 index 000000000..5d5a44957 --- /dev/null +++ b/src/piglet_plugins/pp_ip_api_iface.h @@ -0,0 +1,32 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// pp_ip_api_iface.h author Joel Cornett + +#ifndef PP_IP_API_IFACE_H +#define PP_IP_API_IFACE_H + +#include "lua/lua_iface.h" + +namespace ip +{ +class IpApi; +} + +extern const struct Lua::TypeInterface IpApiIface; + +#endif diff --git a/src/piglet_plugins/pp_logger.cc b/src/piglet_plugins/pp_logger.cc index 365128e4c..c86369b1e 100644 --- a/src/piglet_plugins/pp_logger.cc +++ b/src/piglet_plugins/pp_logger.cc @@ -29,6 +29,7 @@ #include "pp_decode_data_iface.h" #include "pp_event_iface.h" +#include "pp_ip_api_iface.h" #include "pp_packet_iface.h" #include "pp_raw_buffer_iface.h" @@ -67,6 +68,7 @@ bool LoggerPiglet::setup() install(L, RawBufferIface); install(L, DecodeDataIface); + install(L, IpApiIface); install(L, PacketIface); install(L, EventIface); diff --git a/src/piglet_plugins/pp_test.cc b/src/piglet_plugins/pp_test.cc index 165a2eba0..6fe50cd76 100644 --- a/src/piglet_plugins/pp_test.cc +++ b/src/piglet_plugins/pp_test.cc @@ -36,6 +36,7 @@ #include "pp_enc_state_iface.h" #include "pp_event_iface.h" #include "pp_flow_iface.h" +#include "pp_ip_api_iface.h" #include "pp_packet_iface.h" #include "pp_raw_buffer_iface.h" @@ -59,6 +60,7 @@ bool TestPiglet::setup() install(L, EncStateIface); install(L, EventIface); install(L, FlowIface); + install(L, IpApiIface); install(L, PacketIface); install(L, RawBufferIface);