determine verdict. (Conversely, builtin actions don't have an associated
plugin function.)
+=== Developers Guide
+
+Run doc/dev_guide.sh to generate /tmp/dev_guide.html, an annotated guide to
+the source tree.
=== Piglet Test Harness
plugin =
{
type = "piglet",
- version = 1
- }
-
- piglet =
- {
- name = "my_test_for_ipv4_codec",
- type = "codec",
- target = "ipv4",
+ name = "codec::ipv4",
+ use_defaults = true,
test = function()
- local raw_data = RawData.new(1024)
+ local daq_header = DAQHeader.new()
+ local raw_buffer = RawBuffer.new("some data")
local codec_data = CodecData.new()
local decode_data = DecodeData.new()
- codec("decode", raw_data, codec_data, decode_data)
- return true
+ return Codec.decode(
+ daq_header,
+ raw_buffer,
+ codec_data,
+ decode_data
+ )
end
}
-More example tests can be found in the piglet_scripts directory. Refer to these
-examples for furthur usage of Lua wrappers to snort data structures (such as Packet).
-
To run snort in piglet mode, first build snort with the BUILD_PIGLET option turned on
(pass the flag -DBUILD_PIGLET:BOOL=ON in cmake).
The test runner will generate a check-like output, indicating the
the results of each test script.
+=== Piglet Lua API
-=== Developers Guide
+This section documents the API that piglet exposes to Lua.
+Refer to the piglet directory in the source tree for examples of usage.
-Run doc/dev_guide.sh to generate /tmp/dev_guide.html, an annotated guide to
-the source tree.
+Note: Because of the differences between the Lua and C\++ data model and type
+system, not all parameters map directly to the parameters of the underlying
+C\++ member functions. Every effort has been made to keep the mappings consist,
+but there are still some differences. They are documented below.
+
+==== Plugin Instances
+
+For each test, piglet instantiates plugin specified in the ++name++ field of the
+++plugin++ table. The virtual methods of the instance are exposed in a table
+unique to each plugin type. The name of the table is the CamelCase name of the
+plugin type.
+
+For example, codec plugins have a virtual method called ++decode++. This method
+is called like this:
+
+ Codec.decode(...)
+
+*Codec*
+
+* ++Codec.get_data_link_type() -> { int, int, ... }++
+* ++Codec.get_protocol_ids() -> { int, int, ... }++
+* ++Codec.decode(DAQHeader, CodecData, DecodeData) -> bool++
+* ++Codec.log(RawBuffer, uint[lyr_len])++
+* ++Codec.encode(RawBuffer, EncState, Buffer) -> bool++
+* ++Codec.update(uint[flags_hi], uint[flags_lo], RawBuffer, uint[lyr_len] -> int++
+* ++Codec.format(bool[reverse], RawBuffer, DecodeData)++
+
+Differences:
+
+* In ++Codec.update()++, the ++(uint64_t) flags++ parameter has been split into
+++flags_hi++ and ++flags_lo++
+
+*Inspector*
+
+* ++Inspector.configure()++
+* ++Inspector.tinit()++
+* ++Inspector.tterm()++
+* ++Inspector.likes(Packet)++
+* ++Inspector.eval(Packet)++
+* ++Inspector.clear(Packet)++
+* ++Inspector.get_buf_from_key(string[key], Packet, RawBuffer) -> bool++
+* ++Inspector.get_buf_from_id(uint[id], Packet, RawBuffer) -> bool++
+* ++Inspector.get_buf_from_type(uint[type], Packet, RawBuffer) -> bool++
+* ++Inspector.get_splitter(bool[to_server]) -> StreamSplitter++
+
+Differences:
+* In ++Inspector.configure()++, the ++SnortConfig*++ parameter is passed implicitly.
+* the overloaded ++get_buf()++ member function has been split into three separate methods.
+
+*IpsOption*
+
+* ++IpsOption.hash() -> int++
+* ++IpsOption.is_relative() -> bool++
+* ++IpsOption.fp_research() -> bool++
+* ++IpsOption.get_cursor_type() -> int++
+* ++IpsOption.eval(Cursor, Packet) -> int++
+* ++IpsOption.action(Packet)++
+
+*IpsAction*
+
+* ++IpsAction.exec(Packet)++
+
+*Logger*
+
+* ++Logger.open()++
+* ++Logger.close()++
+* ++Logger.reset()++
+* ++Logger.alert(Packet, string[message], Event)++
+* ++Logger.log(Packet, string[message], Event)++
+
+*SearchEngine*
+
+Currently, SearchEngine does not expose any methods.
+
+*SoRule*
+
+Currently, SoRule does not expose any methods.
+
+===== Interface Objects
+
+Many of the plugins take C\++ classes and structs as arguments. These objects
+are exposed to the Lua API as Lua userdata. Exposed objects are instantiated
+by calling the ++new++ method from each object's method table.
+
+For example, the DecodeData object can be instantiated and exposed to Lua
+like this:
+
+ local decode_data = DecodeData.new(...)
+
+Each object also exposes useful methods for getting and setting member variables,
+and calling the C\++ methods contained in the the object. These methods can
+be accessed using the ++:++ accessor syntax:
+
+ decode_data:set({ sp = 80, dp = 3500 })
+
+Since this is just syntactic sugar for passing the object as the first parameter
+of the function ++DecodeData.set++, an equivalent form is:
+
+ decode_data.set(decode_data, { sp = 80, dp = 3500 })
+
+or even:
+
+ DecodeData.set(decode_data, { sp = 80, dp = 3500 })
+
+*Buffer*
+
+* ++Buffer.new(string[data]) -> Buffer++
+* ++Buffer.new(uint[length]) -> Buffer++
+* ++Buffer.new(RawBuffer) -> Buffer++
+* ++Buffer:allocate(uint[length]) -> bool++
+* ++Buffer:clear()++
+
+*CodecData*
+
+* ++CodecData.new() -> Cursor++
+* ++CodecData.new(uint[next_prot_id]) -> Cursor++
+* ++CodecData.new(fields) -> Cursor++
+
+* ++CodecData:get() -> fields++
+* ++CodecData:set(fields)++
+
+++fields++ is a table with the following contents:
+
+* ++next_prot_id++
+* ++lyr_len++
+* ++invalid_bytes++
+* ++proto_bits++
+* ++codec_flags++
+* ++ip_layer_cnt++
+* ++ip6_extension_count++
+* ++curr_ip6_extension++
+* ++ip6_csum_proto++
+
+*Cursor*
+
+* ++Cursor.new() -> Cursor++
+* ++Cursor.new(Packet) -> Cursor++
+* ++Cursor.new(string[data]) -> Cursor++
+* ++Cursor.new(RawBuffer) -> Cursor++
+* ++Cursor:reset()++
+* ++Cursor:reset(Packet)++
+* ++Cursor:reset(string[data])++
+* ++Cursor:reset(RawBuffer)++
+
+*DAQHeader*
+
+* ++DAQHeader.new() -> DAQHeader++
+* ++DAQHeader.new(fields) -> DAQHeader++
+* ++DAQHeader:get() -> fields++
+* ++DAQHeader:set(fields)++
+
+++fields++ is a table with the following contents:
+
+* ++caplen++
+* ++pktlen++
+* ++ingress_index++
+* ++egress_index++
+* ++ingress_group++
+* ++egress_group++
+* ++flags++
+* ++opaque++
+
+*DecodeData*
+* ++DecodeData.new() -> DecodeData++
+* ++DecodeData.new(fields) -> DecodeData++
+* ++DecodeData:reset()++
+* ++DecodeData:get() -> fields++
+* ++DecodeData:set(fields)++
+* ++DecodeData:set_ipv4_hdr(RawBuffer, uint[offset])++
+
+++fields++ is a table with the following contents:
+
+* ++sp++
+* ++dp++
+* ++decode_flags++
+* ++type++
+
+*EncState*
+
+* ++EncState.new() -> EncState++
+* ++EncState.new(uint[flags_lo]) -> EncState++
+* ++EncState.new(uint[flags_lo], uint[flags_hi]) -> EncState++
+* ++EncState.new(uint[flags_lo], uint[flags_hi], uint[next_proto]) -> EncState++
+* ++EncState.new(uint[flags_lo], uint[flags_hi], uint[next_proto], uint[ttl]) -> EncState++
+* ++EncState.new(uint[flags_lo], uint[flags_hi], uint[next_proto], uint[ttl], uint[dsize]) -> EncState++
+
+*Event*
+
+* ++Event.new() -> Event++
+* ++Event.new(fields) -> Event++
+* ++Event:get() -> fields++
+* ++Event:set(fields)++
+
+++fields++ is a table with the following contents:
+
+* ++event_id++
+* ++event_reference++
+* ++sig_info++
+** ++generator++
+** ++id++
+** ++rev++
+** ++class_id++
+** ++priority++
+** ++text_rule++
+** ++num_services++
+
+*Flow*
+
+* ++Flow.new() -> Flow++
+* ++Flow:reset()++
+
+*Packet*
+
+* ++Packet.new() -> Packet++
+* ++Packet.new(string[data]) -> Packet++
+* ++Packet.new(uint[size]) -> Packet++
+* ++Packet.new(fields) -> Packet++
+* ++Packet.new(RawBuffer) -> Packet++
+* ++Packet.new(DAQHeader) -> Packet++
+* ++Packet:set_decode_data(DecodeData)++
+* ++Packet:set_data(uint[offset], uint[length])++
+* ++Packet:set_flow(Flow)++
+* ++Packet:get() -> fields++
+* ++Packet:set() ++
+* ++Packet:set(string[data]) ++
+* ++Packet:set(uint[size]) ++
+* ++Packet:set(fields) ++
+* ++Packet:set(RawBuffer) ++
+* ++Packet:set(DAQHeader) ++
+
+++fields++ is a table with the following contents:
+
+* ++packet_flags++
+* ++xtradata_mask++
+* ++proto_bits++
+* ++application_protocol_ordinal++
+* ++alt_dsize++
+* ++num_layers++
+* ++iplist_id++
+* ++user_policy_id++
+* ++ps_proto++
+
+Note: ++Packet.new()++ and ++Packet:set()++ accept multiple arguments of the
+types described above in any order
+
+*RawBuffer*
+
+* ++RawBuffer.new() -> RawBuffer++
+* ++RawBuffer.new(uint[size]) -> RawBuffer++
+* ++RawBuffer.new(string[data]) -> RawBuffer++
+* ++RawBuffer:size() -> int++
+* ++RawBuffer:resize(uint[size])++
+* ++RawBuffer:write(string[data])++
+* ++RawBuffer:write(string[data], uint[size])++
+* ++RawBuffer:read() -> string++
+* ++RawBuffer:read(uint[end]) -> string++
+* ++RawBuffer:read(uint[start], uint[end]) -> string++
+
+Note: calling ++RawBuffer.new()++ with no arguments returns a RawBuffer of size 0
+
+*StreamSplitter*
+
+* ++StreamSplitter:scan(Flow, RawBuffer) -> int, int++
+* ++StreamSplitter:scan(Flow, RawBuffer, uint[len]) -> int, int++
+* ++StreamSplitter:scan(Flow, RawBuffer, uint[len], uint[flags]) -> int, int++
+* ++StreamSplitter:reassemble(Flow, uint[total], uint[offset], RawBuffer) -> int, RawBuffer++
+* ++StreamSplitter:reassemble(Flow, uint[total], uint[offset], RawBuffer, uint[len]) -> int, RawBuffer++
+* ++StreamSplitter:reassemble(Flow, uint[total], uint[offset], RawBuffer, uint[len], uint[flags]) -> int, RawBuffer++
+* ++StreamSplitter:finish(Flow) -> bool++
+
+Note: StreamSplitter does not have a ++new()++ method, it must be created by an inspector via
+++Inspector.get_splitter()++
end,
decode = function()
- local rb = RawBuffer.new()
+ local daq = DAQHeader.new()
+ local rb = RawBuffer.new("foobar")
local cd = CodecData.new()
local dd = DecodeData.new()
- local daq = DAQHeader.new()
- local rd = RawData.new(rb, daq)
- local rv = Codec.decode(rd, cd, dd)
+ local rv = Codec.decode(daq, rb, cd, dd)
assert(not rv)
end,
local rb = RawBuffer.new(64)
-- FIXIT-H: checksum calculation is failing (temporarily set UPD_COOKED (0x1))
- local rv = Codec.update(1, rb)
+ local rv = Codec.update(0, 1, rb)
assert(rv == 0)
-- FIXIT-H: checksum calculation is failing (temporarily set UPD_COOKED (0x1))
- local rv = Codec.update(1, rb, 64)
+ local rv = Codec.update(0, 1, rb, 64)
assert(rv == 0)
end,
test = function()
dofile(SCRIPT_DIR .. "/common.lua")
return run_all(tests)
- end
+ end,
+ -- FIXIT-L: Need this to keep Inspector.configure() happy
+ use_defaults = true
}
HEADER = [[
local p, rb = get_packet()
local ib = RawBuffer.new()
- local rv = Inspector.get_buf_from_key(0, p, ib)
+ local rv = Inspector.get_buf_from_id(0, p, ib)
assert(not rv)
end,
spl = Inspector.get_splitter(true)
assert(type(spl) == "userdata")
+ end,
+
+ configure = function()
+ assert(Inspector.configure())
+ end,
+
+ tinit = function()
+ Inspector.tinit()
+ end,
+
+ tterm = function()
+ Inspector.tterm()
+ end,
+
+ likes = function()
+ local p = Packet.new()
+ assert(not Inspector.likes(p))
end
}
{
initialize = function()
assert(IpsOption)
+ end,
+
+ hash = function()
+ local rv = IpsOption.hash()
+ end,
+
+ is_relative = function() assert(not IpsOption.is_relative()) end,
+
+ fp_research = function() assert(not IpsOption.fp_research()) end,
+
+ get_cursor_type = function()
+ local rv = IpsOption.get_cursor_type()
+ assert(rv == 1)
+ end,
+
+ eval = function()
+ local rb = RawBuffer.new("foobar")
+ local cur = Cursor.new(rb)
+ local p = Packet.new(rb)
+
+ local rv = IpsOption.eval(cur, p)
+ assert(rv)
+ end,
+
+ action = function()
+ local rb = RawBuffer.new("foobar")
+ local p = Packet.new(rb)
+ IpsOption.action(p)
end
}
{
type = "piglet",
name = "logger::alert_csv",
+ use_defaults = true,
test = function()
Logger.open()
dofile(SCRIPT_DIR .. "/common.lua")
tests =
{
- initialization = function()
- local rb = RawBuffer.new()
+ init_with_raw_buffer = function()
+ local rb = RawBuffer.new("abcdefghijklmnopqrstuvwxyz")
local buf = Buffer.new(rb)
assert(buf)
+ end,
+
+ init_with_string = function()
+ local buf = Buffer.new("abcdefg")
+ assert(buf)
+ end,
+
+ init_with_length = function()
+ local buf = Buffer.new(128)
+ assert(buf)
+ end,
+
+ allocate = function()
+ local buf = Buffer.new(16)
+ assert(buf:allocate(10))
+ assert(not buf:allocate(10))
+ end,
+
+ clear = function()
+ local buf = Buffer.new(16)
+ buf:allocate(16)
+ buf:clear()
+ assert(buf:allocate(10))
+ end,
+
+ to_string = function()
+ local buf = Buffer.new("abcdefgh")
+ buf:allocate(3)
+ local v = tostring(buf)
+ assert(#v == 3)
+ assert(v == "gh\0")
end
}
tests =
{
- initialize = function()
- local rb = RawBuffer.new()
- local p = Packet.new(rb)
- local cur = Cursor.new(p)
+ init_default = function()
+ local cur = Cursor.new()
assert(cur)
+ end,
+
+ init_from_string = function()
+ local cur = Cursor.new("abcdefgh")
+ assert(cur)
+ end,
+
+ init_from_raw_buffer = function()
+ local cur = Cursor.new(RawBuffer.new("abcdefgh"))
+ assert(cur)
+ end,
+
+ reset_default = function()
+ local cur = Cursor.new()
+ cur:reset()
+ end,
+
+ reset_from_string = function()
+ local cur = Cursor.new()
+ cur:reset("abcdefgh")
+ end,
+
+ reset_from_raw_buffer = function()
+ local cur = Cursor.new()
+ cur:reset(RawBuffer.new("abcdefgh"))
end
}
initialize = function()
local es = EncState.new()
assert(es)
+
+ es = EncState.new(0x80000000, 0xffffffff, 2, 24, 128)
+ assert(es)
end
}
tests =
{
- initialize = function()
+ init_default = function()
local event = Event.new()
assert(event)
end,
+ init_with_table = function()
+ local event = Event.new(VALUES)
+ assert_table_eq("get()", VALUES, event:get())
+ end,
+
get_and_set = function()
local event = Event.new()
assert_table_eq("get()", DEFAULT_VALUES, event:get())
assert(p)
end,
- initialize_with_data = function()
+ init_with_string = function()
+ local p = Packet.new("foobar")
+ assert(p)
+ end,
+
+ init_with_size = function()
+ local p = Packet.new(128)
+ assert(p)
+ end,
+
+ init_with_raw_buffer = function()
local rb = RawBuffer.new()
local p = Packet.new(rb)
assert(p)
end,
- initialize_with_daq = function()
- local rb = RawBuffer.new()
+ init_with_daq = function()
local daq = DAQHeader.new()
- local p = Packet.new(rb, daq)
+ local p = Packet.new(daq)
+ assert(p)
+ end,
+
+ init_with_table = function()
+ local p = Packet.new(VALUES)
+ assert_table_eq("get()", VALUES, p:get())
+ end,
+
+ init_with_everything = function()
+ local p = Packet.new("foobar", DAQHeader.new(), { packet_flags = 4 })
assert(p)
end,
assert_table_eq("get()", DEFAULT_VALUES, p:get())
p:set(VALUES)
assert_table_eq("set()", VALUES, p:get())
- end,
-
- set_pkt = function()
- local rb = RawBuffer.new()
- local p = Packet.new()
- p:set_pkt(rb)
- end,
-
- set_daq = function()
- local daq = DAQHeader.new()
- local p = Packet.new()
- p:set_daq(daq)
end
}
+++ /dev/null
-plugin =
-{
- type = "piglet",
- name = "piglet::raw_data",
- test = function()
- -- Put the dofile here so that it doesn't get loaded twice
- dofile(SCRIPT_DIR .. "/common.lua")
- return run_all(tests)
- end
-}
-
-tests =
-{
- initialize = function()
- local rb = RawBuffer.new()
- local daq = DAQHeader.new()
- local rd = RawData.new(rb, daq)
- assert(rd)
- end
-}
add_library (lua STATIC
lua.cc
lua.h
- lua_iface.cc
+ lua_ref.cc
+ lua_ref.h
lua_iface.h
lua_util.cc
lua_util.h
x_include_HEADERS = \
lua.h \
+lua_ref.h \
lua_iface.h \
lua_table.h \
lua_arg.h \
liblua_a_SOURCES = \
lua.cc \
lua.h \
-lua_iface.cc \
+lua_ref.cc \
+lua_ref.h \
lua_iface.h \
lua_util.cc \
lua_util.h \
#include <luajit-2.0/lua.hpp>
+#include "lua_stack.h"
+
namespace Lua
{
-// FIXIT-M: generate better oob error messages
-struct Arg
+class Args
{
+public:
+ template<typename T>
+ using ArgCallback = void (*)(lua_State*, int, T& ud);
+
+ Args(lua_State* _L) : L { _L }, count { lua_gettop(L) } { }
+
+private:
lua_State* L;
- int count;
- inline bool exists(int n)
- { return !lua_isnone(L, n); }
+ struct ArgRef
+ {
+ public:
+ ArgRef(lua_State* _L, int ct, int i) :
+ L { _L }, count { ct }, index { i } { }
- inline void check_type(int n, int type)
- { luaL_checktype(L, n, type); }
+ // We treat nil as !exists
+ inline bool exists()
+ { return (index > 0) && (index <= count) && !lua_isnoneornil(L, index); }
- // Types
- inline bool is_type(int n, int type)
- { return lua_type(L, n) == type; }
+ inline bool is_table()
+ { return exists() && lua_istable(L, index); }
- inline bool is_number(int n)
- { return is_type(n, LUA_TNUMBER); }
+ inline void check_table()
+ { luaL_checktype(L, index, LUA_TTABLE); }
- inline bool is_string(int n)
- { return is_type(n, LUA_TSTRING); }
+ template<typename T>
+ inline void check_table(ArgCallback<T> cb, T& ud)
+ {
+ check_table();
+ cb(L, index, ud);
+ }
- inline bool is_table(int n)
- { return is_type(n, LUA_TTABLE); }
+ template<typename T>
+ inline bool opt_table(ArgCallback<T> cb, T& ud)
+ {
+ if ( exists() )
+ {
+ check_table(cb, ud);
+ return true;
+ }
- inline bool is_boolean(int n)
- { return is_type(n, LUA_TBOOLEAN); }
+ return false;
+ }
- // Table
- inline void check_table(int n)
- { check_type(n, LUA_TTABLE); }
+ inline bool is_function()
+ { return exists() && lua_isfunction(L, index); }
- // Boolean
- inline bool check_boolean(int n)
- {
- check_type(n, LUA_TBOOLEAN);
- return lua_toboolean(L, n);
- }
+ // FIXIT-L: We *may* need to insert checks for userdata, pointers, etc here
- inline bool opt_boolean(int n, bool d = false)
- {
- if ( is_boolean(n) )
- return check_boolean(n);
+ inline bool is_int()
+ { return is<int>(); }
- return d;
- }
+ inline int get_int()
+ { return get<int>(); }
- // String
- inline const char* check_string(int n, size_t* len = nullptr)
- {
- check_type(n, LUA_TSTRING);
- return lua_tolstring(L, n, len);
- }
+ inline int check_int()
+ { return check<int>("expected an integer"); }
- inline const char* opt_string(int n, const char* d, size_t* len = nullptr)
- {
- if ( is_string(n) )
- return check_string(n, len);
+ inline int check_int(int max)
+ {
+ int v = check_int();
+ return argcheck((v <= max), v, "Too big");
+ }
- return d;
- }
+ inline int check_int(int min, int max)
+ {
+ int v = check_int(max);
+ return argcheck((v >= min), v, "Too small");
+ }
- // Integers
- inline lua_Integer check_int(int n)
- {
- check_type(n, LUA_TNUMBER);
- return lua_tointeger(L, n);
- }
+ inline int opt_int(int d = 0)
+ { return ( exists() )? check_int() : d; }
- inline lua_Integer check_int(int n, lua_Integer min, lua_Integer max)
- {
- auto v = check_int(n);
- luaL_argcheck(L, ((v >= min) && (v <= max)), n, "oob");
- return v;
- }
+ inline int opt_int(int d, int max)
+ { return ( exists() )? check_int(max) : d; }
- inline lua_Integer opt_int(int n, lua_Integer d = 0)
- {
- if ( is_number(n) )
- return check_int(n);
+ inline int opt_int(int d, int min, int max)
+ { return ( exists() )? check_int(min, max) : d; }
- return d;
- }
+ inline bool is_size()
+ { return is<unsigned>(); }
- inline lua_Integer opt_int(
- int n, lua_Integer d, lua_Integer min, lua_Integer max)
- {
- auto v = opt_int(n, d);
- luaL_argcheck(L, ((v >= min) && (v <= max)), n, "oob");
- return v;
- }
+ inline unsigned get_size()
+ { return get<unsigned>(); }
- inline unsigned check_size(int n)
- {
- auto v = check_int(n);
- luaL_argcheck(L, (v >= 0), n, "oob");
- return v;
- }
+ inline unsigned check_size()
+ { return check<unsigned>("expected an unsigned integer"); }
- inline unsigned check_size(int n, unsigned max)
- {
- auto v = check_size(n);
- luaL_argcheck(L, (v <= max), n, "oob");
- return v;
- }
+ inline unsigned check_size(unsigned max)
+ {
+ unsigned v = check_size();
+ return argcheck((v <= max), v, "too big");
+ }
- inline unsigned check_size(int n, unsigned min, unsigned max)
- {
- auto v = check_size(n, max);
- luaL_argcheck(L, (v >= min), n, "oob");
- return v;
- }
+ inline unsigned check_size(unsigned min, unsigned max)
+ {
+ unsigned v = check_size(max);
+ return argcheck((v >= min), v, "too small");
+ }
- inline unsigned opt_size(int n, unsigned d = 0)
- {
- if ( is_number(n) )
- return check_size(n);
+ inline unsigned opt_size(unsigned d = 0)
+ { return ( exists() ) ? check_size() : d; }
- return d;
- }
+ inline unsigned opt_size(unsigned d, unsigned max)
+ { return ( exists() ) ? check_size(max) : d; }
- inline unsigned opt_size(int n, unsigned d, unsigned max)
- {
- auto v = opt_size(n, d);
- luaL_argcheck(L, (v <= max), n, "oob");
- return v;
- }
+ inline unsigned opt_size(unsigned d, unsigned min, unsigned max)
+ { return ( exists() ) ? check_size(min, max) : d; }
+
+ inline bool is_string()
+ { return is<const char*>(); }
+
+ inline const char* get_string()
+ { return get<const char*>(); }
+
+ inline const char* check_string()
+ { return check<const char*>("expected a string"); }
+
+ inline const char* check_string(size_t& len)
+ { return check<const char*>("expected a string", len); }
+
+ inline const char* opt_string(const char* d = "")
+ { return ( exists() ) ? check_string() : d; }
+
+ inline const char* opt_string(const char* d, size_t& len)
+ { return ( exists() ) ? check_string(len) : d; }
+
+ inline bool is_bool()
+ { return is<bool>(); }
+
+ inline bool get_bool()
+ { return get<bool>(); }
+
+ inline bool check_bool()
+ { return check<bool>("expected a boolean"); }
- inline unsigned opt_size(int n, unsigned d, unsigned min, unsigned max)
+ inline bool opt_bool(bool d = false)
+ { return ( exists() ) ? check_bool() : d; }
+
+ private:
+ lua_State* L;
+ const int count;
+
+ template<typename T>
+ inline T argcheck(bool cond, T v, const char* msg)
+ {
+ luaL_argcheck(L, exists() && cond, index, msg);
+ return v;
+ }
+
+ template<typename T, typename... Args>
+ inline T check(const char* msg, Args&&... args)
+ {
+ T v;
+ return argcheck(
+ Stack<T>::validate(L, index, v, std::forward<Args>(args)...),
+ v, msg
+ );
+ }
+
+ inline bool is(int type)
+ { return exists() && (type == lua_type(L, index)); }
+
+ template<typename T>
+ inline bool is()
+ { return is(Stack<T>::type()); }
+
+ template<typename T>
+ inline T get()
+ { return Stack<T>::get(L, index); }
+
+ public:
+ const int index;
+ };
+
+public:
+ const int count;
+
+ ArgRef operator[](int i)
{
- auto v = opt_size(n, d, max);
- luaL_argcheck(L, (v >= min), n, "oob");
- return v;
- }
+ if ( i < 0 )
+ i += count + 1;
+
+ // If the index is invalid, mark it as such with 0
+ if ( i < 0 )
+ i = 0;
- Arg(lua_State* state) : L(state)
- { count = lua_gettop(L); }
+ return ArgRef(L, count, i);
+ }
};
}
#endif
+++ /dev/null
-//--------------------------------------------------------------------------
-// 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.
-//--------------------------------------------------------------------------
-// lua_iface.cc author Joel Cornett <jocornet@cisco.com>
-
-#include "lua_iface.h"
-
-#include <luajit-2.0/lua.hpp>
-
-namespace Lua { }
#ifndef LUA_IFACE_H
#define LUA_IFACE_H
-#include <vector>
#include <luajit-2.0/lua.hpp>
#include "lua.h"
-#include "lua_stack.h"
+#include "lua_ref.h"
#include "lua_table.h"
namespace Lua
struct TypeInterface
{
using type = T;
+ using AccessorCallback = void (*)(lua_State*, int, T&);
+
const char* name;
const luaL_Reg* methods;
const luaL_Reg* metamethods;
T& get(lua_State* L, int arg = 1) const
{ return **regurgitate(L, arg); }
+ bool is(lua_State* L, int arg = 1) const
+ {
+ ManageStack ms(L, 1);
+
+ if ( lua_type(L, arg) != LUA_TUSERDATA )
+ return false;
+
+ // Check the registry for metatable with name
+ lua_getfield(L, LUA_REGISTRYINDEX, name);
+ lua_getmetatable(L, arg);
+
+ if ( !lua_rawequal(L, -1, -2) )
+ return false;
+
+ return true;
+ }
+
T** allocate(lua_State*) const;
template<typename... Args>
T& create(lua_State*, Args&&...) const;
void destroy(lua_State*, T** = nullptr) const;
+
+ int default_tostring(lua_State* L) const
+ {
+ lua_pushfstring(L, "%s@0x%p", this->name, &this->get(L));
+ return 1;
+ }
+
+ int default_gc(lua_State* L) const
+ {
+ this->destroy(L);
+ return 0;
+ }
+
+ int default_getter(lua_State* L, AccessorCallback acb) const
+ {
+ auto& self = this->get(L);
+ lua_newtable(L);
+ acb(L, lua_gettop(L), self);
+ return 1;
+ }
+
+ int default_setter(lua_State* L, AccessorCallback acb) const
+ {
+ auto& self = this->get(L, 1);
+ luaL_checktype(L, 2, LUA_TTABLE);
+ acb(L, 2, self);
+ return 0;
+ }
};
template<typename T>
if ( *t )
{
+ remove_refs(L, static_cast<void*>(*t));
delete *t;
t = nullptr;
}
return *static_cast<T*>(const_cast<void*>(lua_topointer(L, idx)));
}
+// -----------------------------------------------------------------------------
+// Library
+// -----------------------------------------------------------------------------
+
+struct Library
+{
+ const char* name;
+ const luaL_Reg* methods;
+};
+
// -----------------------------------------------------------------------------
// Installers
// -----------------------------------------------------------------------------
int inst = lua_gettop(L);
register_with_closure(L, iface.methods, table, inst);
}
+
+static inline void install(lua_State* L, const struct Library& lib)
+{ register_methods(L, lib.methods, lib.name); }
+
}
+
#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// lua_ref.cc author Joel Cornett <jocornet@cisco.com>
+
+#include "lua_ref.h"
+
+#include <luajit-2.0/lua.hpp>
+
+#include "lua.h"
+
+// leave 2 items on the stack (key, ref table)
+// uses 4 slots
+static inline void create_registry_table(lua_State* L, void* p)
+{
+ // key = address of object
+ lua_pushlightuserdata(L, p);
+ // dup key
+ lua_pushvalue(L, -1);
+ // get: registry[key]
+ lua_gettable(L, LUA_REGISTRYINDEX);
+
+ if ( lua_isnil(L, -1) )
+ {
+ // remove nil
+ lua_pop(L, 1);
+ // ref = new ref table
+ lua_newtable(L);
+ // dup key
+ lua_pushvalue(L, -2);
+ // dup ref
+ lua_pushvalue(L, -2);
+ // set: registry[key] = ref
+ lua_settable(L, LUA_REGISTRYINDEX);
+ }
+}
+
+// leaves 1 item on the stack (ref table or nil)
+// uses 1 slot
+static inline void lookup_registry_table(lua_State* L, void* p)
+{
+ lua_pushlightuserdata(L, p);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+}
+
+// leaves nothing extra on the stack
+// uses 1 slot
+static inline void add_entry(lua_State* L, const char* key, int index, int table)
+{
+ lua_pushvalue(L, index);
+ lua_setfield(L, table, key);
+}
+
+// leaves nothing extra on the stack
+// uses 1 slot
+static inline void remove_entry(lua_State* L, const char* key, int table)
+{
+ lua_pushnil(L);
+ lua_setfield(L, table, key);
+}
+
+namespace Lua
+{
+void add_ref(lua_State* L, void* owner, const char* key, int ref_index)
+{
+ Lua::ManageStack ms(L, 4);
+ create_registry_table(L, owner);
+ add_entry(L, key, ref_index, lua_gettop(L));
+}
+
+void remove_ref(lua_State* L, void* owner, const char* key)
+{
+ Lua::ManageStack ms(L, 2);
+ lookup_registry_table(L, owner);
+ if ( !lua_isnil(L, -1) )
+ remove_entry(L, key, lua_gettop(L));
+}
+
+void remove_refs(lua_State* L, void* owner)
+{
+ Lua::ManageStack ms(L, 2);
+ lua_pushlightuserdata(L, owner);
+ lua_pushnil(L);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+}
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-// pp_raw_data_iface.h author Joel Cornett <jocornet@cisco.com>
+// lua_ref.h author Joel Cornett <jocornet@cisco.com>
-#ifndef PP_RAW_DATA_IFACE_H
-#define PP_RAW_DATA_IFACE_H
+#ifndef LUA_REF_H
+#define LUA_REF_H
-#include "lua/lua_iface.h"
+// Keep references to C objects in Lua to prevent premature garbage collection
-struct RawData;
-
-extern const struct Lua::TypeInterface<RawData> RawDataIface;
+struct lua_State;
+namespace Lua
+{
+void add_ref(lua_State*, void*, const char*, int);
+void remove_ref(lua_State*, void*, const char*);
+void remove_refs(lua_State*, void*);
+}
#endif
#include <string>
#include <type_traits>
-
#include <luajit-2.0/lua.hpp>
-#include "lua_util.h"
-
namespace Lua
{
-template<typename T, typename Integral = void>
+template<typename T>
+static inline constexpr bool IsInteger()
+{ return std::is_integral<T>::value && !std::is_same<T, bool>::value; }
+
+template<typename T, typename Integral = void, typename Unsigned = void>
struct Stack {};
+// unsigned integer
template<typename T>
-struct Stack<T, typename std::enable_if<std::is_integral<T>::value>::type>
+struct Stack<T, typename std::enable_if<IsInteger<T>()>::type,
+ typename std::enable_if<std::is_unsigned<T>::value>::type>
{
static inline void push(lua_State* L, const T& v)
- { lua_pushnumber(L, v); }
+ { lua_pushinteger(L, v); }
static inline T get(lua_State* L, int n)
- { return lua_tonumber(L, n); }
+ { return lua_tointeger(L, n); }
static inline constexpr int type()
{ return LUA_TNUMBER; }
+
+ static inline bool validate(lua_State* L, int n, T& v)
+ {
+ if ( lua_type(L, n) != type() )
+ return false;
+
+ lua_Integer tmp = lua_tointeger(L, n);
+ if ( tmp < 0 )
+ return false;
+
+ v = tmp;
+ return true;
+ }
+
+ static inline bool validate(lua_State* L, int n)
+ {
+ T v;
+ return validate(L, n, v);
+ }
};
-template<>
-struct Stack<const char*>
+// integer
+template<typename T>
+struct Stack<T, typename std::enable_if<IsInteger<T>()>::type,
+ typename std::enable_if<!std::is_unsigned<T>::value>::type>
{
- static inline void push(lua_State* L, const char* v)
- { lua_pushstring(L, v); }
+ static inline void push(lua_State* L, const T& v)
+ { lua_pushinteger(L, v); }
- static inline const char* get(lua_State* L, int n)
- { return lua_tostring(L, n); }
+ static inline T get(lua_State* L, int n)
+ { return lua_tointeger(L, n); }
static inline constexpr int type()
- { return LUA_TSTRING; }
+ { return LUA_TNUMBER; }
+
+ static inline bool validate(lua_State* L, int n, T& v)
+ {
+ if ( lua_type(L, n) != type() )
+ return false;
+
+ v = lua_tointeger(L, n);
+ return true;
+ }
+
+ static inline bool validate(lua_State* L, int n)
+ {
+ T v;
+ return validate(L, n, v);
+ }
};
-template<>
-struct Stack<std::string>
+// default
+template<typename T>
+struct Stack<T, typename std::enable_if<!IsInteger<T>()>::type>
{
- static inline void push(lua_State* L, const std::string& v)
- { lua_pushlstring(L, v.c_str(), v.size()); }
+ static inline void push(lua_State*, T);
+ static inline void push(lua_State*, T, size_t);
+
+ static inline T get(lua_State*, int);
+ static inline T get(lua_State*, int, size_t&);
- static inline std::string get(lua_State* L, int n)
+ static inline constexpr int type();
+
+ static inline bool validate(lua_State* L, int n, T& v)
{
- size_t len = 0;
- const char* s = lua_tolstring(L, n, &len);
- return std::string(s, len);
+ if ( lua_type(L, n) != type() )
+ return false;
+
+ v = get(L, n);
+ return true;
}
- static inline constexpr int type()
- { return LUA_TSTRING; }
+ static inline bool validate(lua_State* L, int n)
+ {
+ T v;
+ return validate(L, n, v);
+ }
+
+ static inline bool validate(lua_State*, int, T&, size_t&);
};
+// const char*
+template<>
+inline void Stack<const char*>::push(lua_State* L, const char* s)
+{ lua_pushstring(L, s); }
+
+template<>
+inline void Stack<const char*>::push(lua_State* L, const char* s, size_t len)
+{ lua_pushlstring(L, s, len); }
+
template<>
-struct Stack<bool>
+inline const char* Stack<const char*>::get(lua_State* L, int n)
+{ return lua_tostring(L, n); }
+
+template<>
+inline const char* Stack<const char*>::get(lua_State* L, int n, size_t& len)
+{ return lua_tolstring(L, n, &len); }
+
+template<>
+inline constexpr int Stack<const char*>::type()
+{ return LUA_TSTRING; }
+
+template<>
+inline bool Stack<const char*>::validate(
+ lua_State* L, int n, const char*& v, size_t& len)
{
- static inline void push(lua_State* L, const bool& v)
- { lua_pushboolean(L, v); }
+ if ( lua_type(L, n) != type() )
+ return false;
- static inline bool get(lua_State* L, int n)
- { return lua_toboolean(L, n); }
+ v = get(L, n, len);
+ return true;
+}
- static inline constexpr int type()
- { return LUA_TBOOLEAN; }
-};
+// string
+template<>
+inline void Stack<std::string>::push(lua_State* L, std::string s)
+{ lua_pushlstring(L, s.c_str(), s.length()); }
+
+template<>
+inline std::string Stack<std::string>::get(lua_State* L, int n)
+{
+ size_t len = 0;
+ const char* s = lua_tolstring(L, n, &len);
+ return std::string(s, len);
+}
+
+template<>
+inline constexpr int Stack<std::string>::type()
+{ return LUA_TSTRING; }
+
+// bool
+template<>
+inline void Stack<bool>::push(lua_State* L, bool v)
+{ lua_pushboolean(L, v); }
+
+template<>
+inline bool Stack<bool>::get(lua_State* L, int n)
+{ return lua_toboolean(L, n); }
+
+template<>
+inline constexpr int Stack<bool>::type()
+{ return LUA_TBOOLEAN; }
}
#endif
return rv;
}
+ template<typename T>
+ inline bool get_default(const char* k, T& v, T d = 0)
+ {
+ if ( !get_field<T>(k, v) )
+ {
+ v = d;
+ return false;
+ }
+
+ return true;
+ }
+
template<typename T>
inline bool raw_get_field(const char* k, T& v)
{
#include <luajit-2.0/lua.hpp>
#include "ips_manager.h"
+#include "plugin_manager.h"
#include "lua/lua.h"
#include "lua/lua_util.h"
-#include "plugin_manager.h"
#include "framework/ips_option.h"
#include "framework/logger.h"
#include "framework/lua_api.h"
return nullptr;
}
-static BasePlugin* instantiate(Lua::State& lua, PlugType key, std::string name)
+static BasePlugin* instantiate(
+ Lua::State& lua, PlugType key, std::string name, bool use_defaults)
{
auto piglet_api = find_piglet(key);
}
Module* m;
-
- if ( key == PT_IPS_OPTION )
+ if ( key == PT_IPS_OPTION || use_defaults )
// FIXIT-M: this is just a workaround.
// Need to be able to get parsed rule module
m = ModuleManager::get_default_module(name.c_str(), snort_conf);
{ plugins[api->target] = api; }
BasePlugin* Manager::instantiate(
- Lua::State& lua, const string& target, string& type, string& name)
+ Lua::State& lua, const string& target,
+ string& type, string& name, bool use_defaults)
{
PlugType pt = PT_MAX;
split_key(target, type, name);
return nullptr;
}
- return ::Piglet::instantiate(lua, pt, name);
+ return ::Piglet::instantiate(lua, pt, name, use_defaults);
}
void Manager::destroy(BasePlugin* p)
static void add_plugin(Api*);
static BasePlugin* instantiate(
- Lua::State&, const std::string&, std::string&, std::string&);
+ Lua::State&, const std::string&,
+ std::string&, std::string&, bool = false);
static void destroy(BasePlugin*);
{
Lua::ManageStack ms(L);
+ if ( setup_globals(L, t) )
+ {
+ t.set_error("couldn't setup globals");
+ return true;
+ }
+
if ( load_chunk(L, t.chunk) )
{
t.set_error("couldn't load test chunk");
Lua::Table table(L, -1);
table.get_field("description", t.description);
+ table.get_field("use_defaults", t.use_defaults);
- return setup_globals(L, t);
+ return false;
}
static bool run_test(lua_State* L, Test& t)
return;
}
- auto p = Manager::instantiate(state, t.chunk.target, t.type, t.name);
+ auto p = Manager::instantiate(
+ state, t.chunk.target, t.type, t.name, t.use_defaults);
// FIXIT-L: This injection is a hack so we can log the test header
// with all the parsed information filled in
std::string type;
std::string name;
std::string description;
+ bool use_defaults = false;
std::vector<std::string> messages;
set (
PP_CODEC_DEPENDENCIES
pp_codec_data_iface.cc
- pp_raw_data_iface.cc
pp_enc_state_iface.cc
pp_buffer_iface.cc
pp_event_iface.cc
interface_list = \
pp_codec_data_iface.cc \
-pp_raw_data_iface.cc \
pp_enc_state_iface.cc \
pp_buffer_iface.cc \
pp_event_iface.cc \
piglet_plugins_common contains utilities for working with the Lua C API and
Lua interfaces for some useful Snort data structures (Packet, DecodeData).
-There is also an interface called RawData. This is essentially a wrapper
-around a vector<char>.
+There is also an interface called RawBuffer. This is essentially a wrapper
+around a std::string.
#include <luajit-2.0/lua.hpp>
#include "framework/codec.h"
+#include "lua/lua_arg.h"
+#include "lua/lua_ref.h"
#include "pp_raw_buffer_iface.h"
static const luaL_Reg methods[] =
"new",
[](lua_State* L)
{
- // FIXIT-M: need a way to track refs to other
- // objs in lua so the don't get gc'd too early
- auto& rb = RawBufferIface.get(L);
- BufferIface.create(
- L,
- const_cast<uint8_t*>(
- reinterpret_cast<const uint8_t*>(rb.data())),
- rb.size()
- );
+ Lua::Args args(L);
+
+ RawBuffer* rb;
+ size_t len;
+ int idx = 1;
+
+ if ( args[1].is_string() )
+ {
+ // Create a RawBuffer object to back the string
+ len = 0;
+ const char* s = args[1].check_string(len);
+ rb = &RawBufferIface.create(L, s, len);
+ idx = lua_gettop(L);
+ }
+ else if ( args[1].is_size() )
+ {
+ len = args[1].check_size();
+ // Create a RawBuffer object to back the string
+ rb = &RawBufferIface.create(L, len, '\0');
+ idx = lua_gettop(L);
+ }
+ else
+ {
+ rb = &RawBufferIface.get(L, 1);
+ }
+
+ auto& self = BufferIface.create(L, get_mutable_data(*rb), rb->size());
+ // Save a reference to the RawBuffer
+ // FIXIT-M: Integrate this into the interface code so we don't
+ // have to do this explicitly
+ Lua::add_ref(L, &self, "data", idx);
return 1;
}
},
+ {
+ "allocate",
+ [](lua_State* L)
+ {
+ Lua::Args args(L);
+ auto& self = BufferIface.get(L, 1);
+ uint32_t len = args[2].check_size();
+ bool result = self.allocate(len);
+ lua_pushboolean(L, result);
+ return 1;
+ }
+ },
+ {
+ "clear",
+ [](lua_State* L)
+ {
+ BufferIface.get(L).clear();
+ return 0;
+ }
+ },
{ nullptr, nullptr }
};
[](lua_State* L)
{
auto& self = BufferIface.get(L);
- lua_pushfstring(L, "%s@%p", BufferIface.name, &self);
+ lua_pushlstring(L, reinterpret_cast<const char*>(self.data()),
+ self.size());
+ // lua_pushfstring(L, "%s@%p", BufferIface.name, &self);
return 1;
}
#include "pp_decode_data_iface.h"
#include "pp_enc_state_iface.h"
#include "pp_raw_buffer_iface.h"
-#include "pp_raw_data_iface.h"
#include "pp_codec_iface.h"
install(L, RawBufferIface);
install(L, DecodeDataIface);
- install(L, RawDataIface);
install(L, CodecDataIface);
install(L, EncStateIface);
install(L, BufferIface);
#include "pp_codec_data_iface.h"
+#include <string.h>
#include <luajit-2.0/lua.hpp>
#include "framework/codec.h"
#include "lua/lua_table.h"
#include "lua/lua_arg.h"
-static void set_fields(lua_State* L, int tindex, CodecData& cd)
+static void set_fields(lua_State* L, int tindex, CodecData& self)
{
Lua::Table table(L, tindex);
- table.get_field("next_prot_id", cd.next_prot_id);
- table.get_field("lyr_len", cd.lyr_len);
- table.get_field("invalid_bytes", cd.invalid_bytes);
- table.get_field("proto_bits", cd.proto_bits);
- table.get_field("codec_flags", cd.codec_flags);
- table.get_field("ip_layer_cnt", cd.ip_layer_cnt);
- table.get_field("ip6_extension_count", cd.ip6_extension_count);
- table.get_field("curr_ip6_extension", cd.curr_ip6_extension);
- table.get_field("ip6_csum_proto", cd.ip6_csum_proto);
+ table.get_field("next_prot_id", self.next_prot_id);
+ table.get_field("lyr_len", self.lyr_len);
+ table.get_field("invalid_bytes", self.invalid_bytes);
+ table.get_field("proto_bits", self.proto_bits);
+ table.get_field("codec_flags", self.codec_flags);
+ table.get_field("ip_layer_cnt", self.ip_layer_cnt);
+ table.get_field("ip6_extension_count", self.ip6_extension_count);
+ table.get_field("curr_ip6_extension", self.curr_ip6_extension);
+ table.get_field("ip6_csum_proto", self.ip6_csum_proto);
+}
+
+static void get_fields(lua_State* L, int tindex, CodecData& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.set_field("next_prot_id", self.next_prot_id);
+ table.set_field("lyr_len", self.lyr_len);
+ table.set_field("invalid_bytes", self.invalid_bytes);
+ table.set_field("proto_bits", self.proto_bits);
+ table.set_field("codec_flags", self.codec_flags);
+ table.set_field("ip_layer_cnt", self.ip_layer_cnt);
+ table.set_field("ip6_extension_count", self.ip6_extension_count);
+ table.set_field("curr_ip6_extension", self.curr_ip6_extension);
+ table.set_field("ip6_csum_proto", self.ip6_csum_proto);
}
static const luaL_Reg methods[] =
"new",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = CodecDataIface.create(L, 0);
+ memset(&self, 0, sizeof(self));
- if ( arg.count )
- {
- if ( arg.is_table(1) )
- set_fields(L, 1, self);
- else
- self.next_prot_id = arg.check_size(1);
- }
+ if ( args[1].is_table() )
+ args[1].check_table(set_fields, self);
+ else if ( args[1].is_size() )
+ self.next_prot_id = args[1].check_size();
return 1;
}
{
"get",
[](lua_State* L)
- {
- auto& self = CodecDataIface.get(L);
- lua_newtable(L);
-
- Lua::Table table(L, lua_gettop(L));
-
- table.set_field("next_prot_id", self.next_prot_id);
- table.set_field("lyr_len", self.lyr_len);
- table.set_field("invalid_bytes", self.invalid_bytes);
- table.set_field("proto_bits", self.proto_bits);
- table.set_field("codec_flags", self.codec_flags);
- table.set_field("ip_layer_cnt", self.ip_layer_cnt);
- table.set_field("ip6_extension_count", self.ip6_extension_count);
- table.set_field("curr_ip6_extension", self.curr_ip6_extension);
- table.set_field("ip6_csum_proto", self.ip6_csum_proto);
-
- return 1;
- }
+ { return CodecDataIface.default_getter(L, get_fields); }
},
{
"set",
[](lua_State* L)
- {
- Lua::Arg arg(L);
-
- auto& self = CodecDataIface.get(L, 1);
-
- arg.check_table(2);
- set_fields(L, 2, self);
-
- return 0;
- }
+ { return CodecDataIface.default_setter(L, set_fields); }
},
{ nullptr, nullptr }
};
{
"__tostring",
[](lua_State* L)
- {
- auto& self = CodecDataIface.get(L);
- lua_pushfstring(L, "%s@%p", CodecDataIface.name, &self);
-
- return 1;
- }
+ { return CodecDataIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- CodecDataIface.destroy(L);
- return 0;
- }
+ { return CodecDataIface.default_gc(L); }
},
{ nullptr, nullptr }
};
#include <limits>
#include <vector>
+#include <assert.h>
#include <luajit-2.0/lua.hpp>
#include "framework/codec.h"
#include "pp_buffer_iface.h"
#include "pp_codec_data_iface.h"
+#include "pp_daq_pkthdr_iface.h"
#include "pp_decode_data_iface.h"
#include "pp_enc_state_iface.h"
#include "pp_raw_buffer_iface.h"
-#include "pp_raw_data_iface.h"
-
-static std::vector<int> data_link_types;
-static std::vector<uint16_t> protocol_ids;
+// FIXIT-M: This should be its own object
static const ip::IpApi ip_api {};
struct TextLogWrapper
TextLog* text_log;
TextLogWrapper(const char* name)
- { text_log = TextLog_Init(name); }
+ {
+ text_log = TextLog_Init(name);
+ assert(text_log);
+ }
~TextLogWrapper()
{
"decode",
[](lua_State* L)
{
- auto& rd = RawDataIface.get(L, 1);
- auto& cd = CodecDataIface.get(L, 2);
- auto& dd = DecodeDataIface.get(L, 3);
+ bool result;
+
+ auto& daq = DAQHeaderIface.get(L, 1);
+ auto& cd = CodecDataIface.get(L, 3);
+ auto& dd = DecodeDataIface.get(L, 4);
auto& self = CodecIface.get(L);
- bool result = self.decode(rd, cd, dd);
+ if ( RawBufferIface.is(L, 2) )
+ {
+ RawData rd(&daq, get_data(RawBufferIface.get(L, 2)));
+ result = self.decode(rd, cd, dd);
+ }
+ else
+ {
+ size_t len = 0;
+ RawData rd(
+ &daq,
+ reinterpret_cast<const uint8_t*>(
+ luaL_checklstring(L, 2, &len)
+ )
+ );
+
+ result = self.decode(rd, cd, dd);
+ }
+
lua_pushboolean(L, result);
return 1;
"log",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& rb = RawBufferIface.get(L, 1);
- uint16_t lyr_len = arg.opt_size(2, rb.size(), rb.size());
+ uint16_t lyr_len = args[2].opt_size(rb.size(), rb.size());
auto& self = CodecIface.get(L);
TextLogWrapper tl_wrap("stdout");
- // FIXIT-L: Do we need to check for null tl_wrap.text_log?
- self.log(
- tl_wrap.text_log,
- reinterpret_cast<const uint8_t*>(rb.data()),
- lyr_len
- );
+ self.log(tl_wrap.text_log, get_data(rb), lyr_len);
return 0;
}
auto& self = CodecIface.get(L);
- bool result = self.encode(
- reinterpret_cast<const uint8_t*>(rb.data()), rb.size(), es, b);
+ bool result = self.encode(get_data(rb), rb.size(), es, b);
lua_pushboolean(L, result);
"update",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- // FIXIT-M: We can't represent all flags int lua_Integer type
- uint64_t flags = arg.check_size(1);
- auto& rb = RawBufferIface.get(L, 2);
- uint16_t lyr_len = arg.opt_size(3, 0, rb.size());
+ uint32_t flags_hi = args[1].check_size();
+ uint32_t flags_lo = args[2].check_size();
+ auto& rb = RawBufferIface.get(L, 3);
+
+ // FIXIT-L: Args vs Iface is not orthogonal
+ uint16_t lyr_len = args[4].opt_size(0, rb.size());
auto& self = CodecIface.get(L);
uint32_t updated_len = 0;
- self.update(
- ip_api, flags,
- const_cast<uint8_t*>(
- reinterpret_cast<const uint8_t*>(rb.data())),
- lyr_len, updated_len
- );
+ uint64_t flags = (static_cast<uint64_t>(flags_hi) << 8) | flags_lo;
+
+ self.update(ip_api, flags, get_mutable_data(rb), lyr_len,
+ updated_len);
lua_pushinteger(L, updated_len);
"format",
[](lua_State* L)
{
- bool reverse = lua_toboolean(L, 1);
+ Lua::Args args(L);
+
+ bool reverse = args[1].get_bool();
auto& rb = RawBufferIface.get(L, 2);
auto& dd = DecodeDataIface.get(L, 3);
auto& self = CodecIface.get(L);
- self.format(
- reverse,
- const_cast<uint8_t*>(
- reinterpret_cast<const uint8_t*>(rb.data())),
- dd
- );
+
+ self.format(reverse, get_mutable_data(rb), dd);
return 0;
}
#include <luajit-2.0/lua.hpp>
#include "framework/cursor.h"
+#include "lua/lua_arg.h"
+#include "lua/lua_ref.h"
+#include "protocols/packet.h"
#include "pp_packet_iface.h"
+#include "pp_raw_buffer_iface.h"
+
+static void reset_from_packet(
+ lua_State* L, Cursor& self, Packet& p, int p_idx)
+{
+ self.reset(&p);
+ Lua::add_ref(L, &self, "data", p_idx);
+}
+
+static void reset_from_raw_buffer(
+ lua_State* L, Cursor& self, RawBuffer& rb, int rb_idx)
+{
+ Packet p;
+ p.reset();
+
+ p.data = get_data(rb);
+ p.dsize = rb.size();
+
+ self.reset(&p);
+ Lua::add_ref(L, &self, "data", rb_idx);
+}
static const luaL_Reg methods[] =
{
"new",
[](lua_State* L)
{
- auto& p = PacketIface.get(L);
- CursorIface.create(L, &p);
+ Lua::Args args(L);
+ Packet p;
+ p.reset();
+
+ auto& self = CursorIface.create(L, &p);
+
+ if ( args.count )
+ {
+ if ( PacketIface.is(L, 1) )
+ {
+ reset_from_packet(L, self, PacketIface.get(L, 1), 1);
+ }
+ else if ( args[1].is_string() )
+ {
+ size_t len = 0;
+ const char* s = args[1].check_string(len);
+ auto& rb = RawBufferIface.create(L, s, len);
+ reset_from_raw_buffer(L, self, rb, lua_gettop(L));
+ lua_pop(L, 1);
+ }
+ else
+ {
+ reset_from_raw_buffer(L, self, RawBufferIface.get(L, 1), 1);
+ }
+ }
return 1;
}
},
+ {
+ "reset",
+ [](lua_State* L)
+ {
+ Lua::Args args(L);
+
+ auto& self = CursorIface.get(L, 1);
+
+ if ( args.count > 1 )
+ {
+ if ( PacketIface.is(L, 2) )
+ {
+ auto& p = PacketIface.get(L, 2);
+ reset_from_packet(L, self, p, 2);
+ }
+ else
+ {
+ if ( args[2].is_string() )
+ {
+ size_t len = 0;
+ const char* s = args[2].check_string(len);
+ auto& rb = RawBufferIface.create(L, s, len);
+ reset_from_raw_buffer(L, self, rb, lua_gettop(L));
+ }
+ else
+ {
+ auto& rb = RawBufferIface.get(L, 2);
+ reset_from_raw_buffer(L, self, rb, 2);
+ }
+ }
+ }
+ else
+ {
+ Packet p;
+
+ p.reset();
+ self.reset(&p);
+
+ Lua::remove_ref(L, &self, "data");
+ }
+
+ return 0;
+ }
+ },
{ nullptr, nullptr }
};
{
"__tostring",
[](lua_State* L)
- {
- auto& self = CursorIface.get(L);
- lua_pushfstring(L, "%s@%p", CursorIface.name, &self);
-
- return 1;
- }
+ { return CursorIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- CursorIface.destroy(L);
- return 0;
- }
+ { return CursorIface.default_gc(L); }
},
{ nullptr, nullptr }
};
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//--------------------------------------------------------------------------
-// pp_daq_pkthdr_iface.cc author Joel Cornett <jocornet@cisco.com>
+// pp_codec_data_iface.cc author Joel Cornett <jocornet@cisco.com>
#include "pp_daq_pkthdr_iface.h"
#include "config.h"
#endif
+#include <string.h>
#include <luajit-2.0/lua.hpp>
-#include "lua/lua_table.h"
-
extern "C" {
#include <daq.h>
}
-static void daq_header_set(lua_State* L, int tindex, struct _daq_pkthdr& daq)
+#include "lua/lua_arg.h"
+#include "lua/lua_table.h"
+
+
+static void set_fields(lua_State* L, int tindex, struct _daq_pkthdr& self)
{
Lua::Table table(L, tindex);
- table.get_field("caplen", daq.caplen);
- table.get_field("pktlen", daq.pktlen);
- table.get_field("ingress_index", daq.ingress_index);
- table.get_field("egress_index", daq.egress_index);
- table.get_field("ingress_group", daq.ingress_group);
- table.get_field("egress_group", daq.egress_group);
- table.get_field("flags", daq.flags);
- table.get_field("opaque", daq.opaque);
+ table.get_field("caplen", self.caplen);
+ table.get_field("pktlen", self.pktlen);
+ table.get_field("ingress_index", self.ingress_index);
+ table.get_field("egress_index", self.egress_index);
+ table.get_field("ingress_group", self.ingress_group);
+ table.get_field("egress_group", self.egress_group);
+ table.get_field("flags", self.flags);
+ table.get_field("opaque", self.opaque);
#ifdef HAVE_DAQ_ADDRESS_SPACE_ID
#ifdef HAVE_DAQ_FLOW_ID
- table.get_field("flow_id", daq.flow_id);
+ table.get_field("flow_id", self.flow_id);
#endif
- table.get_field("address_space_id", daq.address_space_id);
+ table.get_field("address_space_id", self.address_space_id);
#endif
// FIXIT-L: Do we want to be able to set the priv_ptr field?
}
+static void get_fields(lua_State* L, int tindex, struct _daq_pkthdr& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.set_field("caplen", self.caplen);
+ table.set_field("pktlen", self.pktlen);
+ table.set_field("ingress_index", self.ingress_index);
+ table.set_field("egress_index", self.egress_index);
+ table.set_field("ingress_group", self.ingress_group);
+ table.set_field("egress_group", self.egress_group);
+ table.set_field("flags", self.flags);
+ table.set_field("opaque", self.opaque);
+#ifdef HAVE_DAQ_ADDRESS_SPACE_ID
+#ifdef HAVE_DAQ_FLOW_ID
+ table.set_field("flow_id", self.flow_id);
+#endif
+ table.set_field("address_space_id", self.address_space_id);
+#endif
+}
+
static const luaL_Reg methods[] =
{
{
"new",
[](lua_State* L)
{
+ Lua::Args args(L);
+
auto& self = DAQHeaderIface.create(L);
+ memset(&self, 0, sizeof(self));
- // If we had an initializer argument, there
- // will be two items on the stack now
- if ( lua_gettop(L) > 1 )
- {
- luaL_checktype(L, 1, LUA_TTABLE);
- daq_header_set(L, 1, self);
- }
+ args[1].opt_table(set_fields, self);
return 1;
}
{
"get",
[](lua_State* L)
- {
- auto& self = DAQHeaderIface.get(L);
- lua_newtable(L);
-
- Lua::Table table(L, lua_gettop(L));
-
- table.set_field("caplen", self.caplen);
- table.set_field("pktlen", self.pktlen);
- table.set_field("ingress_index", self.ingress_index);
- table.set_field("egress_index", self.egress_index);
- table.set_field("ingress_group", self.ingress_group);
- table.set_field("egress_group", self.egress_group);
- table.set_field("flags", self.flags);
- table.set_field("opaque", self.opaque);
-#ifdef HAVE_DAQ_ADDRESS_SPACE_ID
-#ifdef HAVE_DAQ_FLOW_ID
- table.set_field("flow_id", self.flow_id);
-#endif
- table.set_field("address_space_id", self.address_space_id);
-#endif
-
- // FIXIT-L: Do we want to be able to read the priv_ptr field?
-
- return 1;
- }
+ { return DAQHeaderIface.default_getter(L, get_fields); }
},
{
"set",
[](lua_State* L)
- {
- auto& self = DAQHeaderIface.get(L, 1);
- luaL_checktype(L, 2, LUA_TTABLE);
-
- daq_header_set(L, 2, self);
-
- return 0;
- }
+ { return DAQHeaderIface.default_setter(L, set_fields); }
},
{ nullptr, nullptr }
};
{
"__tostring",
[](lua_State* L)
- {
- auto& self = DAQHeaderIface.get(L);
- lua_pushfstring(L, "%s@%p", DAQHeaderIface.name, &self);
-
- return 1;
- }
+ { return DAQHeaderIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- DAQHeaderIface.destroy(L);
- return 0;
- }
+ { return DAQHeaderIface.default_gc(L); }
},
{ nullptr, nullptr }
};
#include "pp_decode_data_iface.h"
+#include <assert.h>
#include <luajit-2.0/lua.hpp>
#include "framework/decode_data.h"
#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_raw_buffer_iface.h"
-static void set_fields(lua_State* L, int tindex, DecodeData& dd)
+// FIXIT-H: Add Internet Header objects
+// FIXIT-H: Add Enum Interface
+static void set_fields(lua_State* L, int tindex, DecodeData& self)
{
Lua::Table table(L, tindex);
- table.get_field("sp", dd.sp);
- table.get_field("dp", dd.dp);
- table.get_field("decode_flags", dd.decode_flags);
+ table.get_field("sp", self.sp);
+ table.get_field("dp", self.dp);
+ table.get_field("decode_flags", self.decode_flags);
uint8_t pkt_type = 0;
table.get_field("type", pkt_type);
- dd.type = static_cast<PktType>(pkt_type);
+ self.type = static_cast<PktType>(pkt_type);
+}
+
+static void get_fields(lua_State* L, int tindex, DecodeData& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.set_field("sp", self.sp);
+ table.set_field("dp", self.dp);
+ table.set_field("decode_flags", self.decode_flags);
+ table.set_field("type", static_cast<uint8_t>(self.type));
}
static const luaL_Reg methods[] =
"new",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = DecodeDataIface.create(L);
+ self.reset();
- if ( arg.count )
- {
- arg.check_table(1);
- set_fields(L, 1, self);
- }
+ args[1].opt_table(set_fields, self);
return 1;
}
"reset",
[](lua_State* L)
{
- auto& self = DecodeDataIface.get(L);
- self.reset();
-
+ DecodeDataIface.get(L).reset();
return 0;
}
},
{
"set",
[](lua_State* L)
- {
- Lua::Arg arg(L);
-
- auto& self = DecodeDataIface.get(L, 1);
-
- arg.check_table(2);
- set_fields(L, 2, self);
-
- return 0;
- }
+ { return DecodeDataIface.default_setter(L, set_fields); }
},
{
"get",
[](lua_State* L)
- {
- auto& self = DecodeDataIface.get(L);
- lua_newtable(L);
-
- Lua::Table table(L, lua_gettop(L));
- table.set_field("sp", self.sp);
- table.set_field("dp", self.dp);
- table.set_field("decode_flags", self.decode_flags);
- table.set_field(
- "type",
- static_cast<uint8_t>(self.type)
- );
-
- return 1;
- }
+ { return DecodeDataIface.default_getter(L, get_fields); }
},
// FIXIT-L: Need a more sophisticated interface to decode data ip_api
{
"set_ipv4_hdr",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = DecodeDataIface.get(L, 1);
auto& rb = RawBufferIface.get(L, 2);
- size_t offset = arg.opt_size(3, 0, rb.size());
+ size_t offset = args[3].opt_size(0, rb.size());
// Need enough room for an IPv4 header
if ( (rb.size() - offset) < sizeof(IP4Hdr) )
{
"__tostring",
[](lua_State* L)
- {
- auto& self = DecodeDataIface.get(L);
- lua_pushfstring(L, "%s@%p", DecodeDataIface.name, &self);
- return 1;
- }
+ { return DecodeDataIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- DecodeDataIface.destroy(L);
- return 0;
- }
+ { return DecodeDataIface.default_gc(L); }
},
{ nullptr, nullptr }
};
#include <luajit-2.0/lua.hpp>
#include "framework/codec.h"
+#include "lua/lua_arg.h"
#include "protocols/ip.h"
+// FIXIT-H: This should also be its own object (copyable)
static const class ip::IpApi ip_api {};
+static inline uint64_t get_encode_flag(uint32_t hi, uint32_t lo)
+{ return (static_cast<uint64_t>(hi) << 4) | lo; }
+
static const luaL_Reg methods[] =
{
{
"new",
[](lua_State* L)
{
- EncStateIface.create(L, ip_api, 0, 0, 0, 0);
+ Lua::Args args(L);
+
+ uint32_t efl_hi = args[1].opt_size();
+ uint32_t efl_lo = args[2].opt_size();
+ uint8_t next_proto = args[3].opt_size();
+ uint8_t ttl = args[4].opt_size();
+ uint16_t dsize = args[5].opt_int();
+
+ EncStateIface.create(L, ip_api, get_encode_flag(efl_hi, efl_lo),
+ next_proto, ttl, dsize);
+
return 1;
}
},
+ // FIXIT-L: Add get and set methods
{ nullptr, nullptr }
};
{
"__tostring",
[](lua_State* L)
- {
- auto& self = EncStateIface.get(L);
- lua_pushfstring(L, "%s@%p", EncStateIface.name, &self);
-
- return 1;
- }
+ { return EncStateIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- EncStateIface.destroy(L);
- return 0;
- }
+ { return EncStateIface.default_gc(L); }
},
{ nullptr, nullptr }
};
#include "detection/signature.h"
#include "events/event.h"
+#include "lua/lua_arg.h"
#include "lua/lua_table.h"
+#include "pp_raw_buffer_iface.h"
+// FIXIT-H: Should be its own object
static struct SigInfo* create_sig_info()
{
auto si = new SigInfo;
return si;
}
+static void set_fields(lua_State* L, int tindex, Event& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.get_field("event_id", self.event_id);
+ table.get_field("event_reference", self.event_reference);
+
+ const char* s = nullptr;
+ // FIXIT-L: Shouldn't need both conditions
+ if ( table.get_field("alt_msg", s) && s )
+ {
+ self.alt_msg = RawBufferIface.create(L, s).c_str();
+ Lua::add_ref(L, &self, "alt_msg", lua_gettop(L));
+ lua_pop(L, 1);
+ }
+}
+
+static void get_fields(lua_State* L, int tindex, Event& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.set_field("event_id", self.event_id);
+ table.set_field("event_reference", self.event_reference);
+
+ if ( self.alt_msg )
+ table.set_field("alt_msg", self.alt_msg);
+}
+
static const luaL_Reg methods[] =
{
{
"new",
[](lua_State* L)
{
+ Lua::Args args(L);
+
auto& self = EventIface.create(L);
+ // FIXIT-M: This should be a separate object
+ // (to make resource tracking more uniform)
self.sig_info = create_sig_info();
+ args[1].opt_table(set_fields, self);
+
return 1;
}
},
auto& self = EventIface.get(L);
lua_newtable(L);
- Lua::Table table(L, lua_gettop(L));
- table.set_field("event_id", self.event_id);
- table.set_field("event_reference", self.event_reference);
+ get_fields(L, lua_gettop(L), self);
auto si = self.sig_info;
si_table.set_field("text_rule", si->text_rule);
si_table.set_field("num_services", si->num_services);
- table.set_field_from_stack("sig_info", si_table.index);
+ Lua::Table(L, 2).set_field_from_stack("sig_info", si_table.index);
}
return 1;
si_table.get_field("num_services", si->num_services);
}
- table.get_field("event_id", self.event_id);
- table.get_field("event_reference", self.event_reference);
+ set_fields(L, 2, self);
return 0;
}
{
"__tostring",
[](lua_State* L)
- {
- auto& self = EventIface.get(L);
- lua_pushfstring(L, "%s@%p", EventIface.name, &self);
-
- return 1;
- }
+ { return EventIface.default_tostring(L); }
},
{
"__gc",
"new",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- PktType type = static_cast<PktType>(arg.opt_size(1));
+ PktType type = static_cast<PktType>(args[1].opt_size());
FlowIface.create(L).init(type);
#include "framework/inspector.h"
#include "lua/lua_arg.h"
+#include "main/snort_config.h"
#include "pp_packet_iface.h"
#include "pp_raw_buffer_iface.h"
#include "pp_stream_splitter_iface.h"
static const luaL_Reg methods[] =
{
+ {
+ "configure",
+ [](lua_State* L)
+ {
+ auto& self = InspectorIface.get(L);
+ // FIXIT-L: Do we need an opaque SnortConfig interface?
+ bool result = self.configure(snort_conf);
+ lua_pushboolean(L, result);
+ return 1;
+ }
+ },
+ {
+ "tinit",
+ [](lua_State* L)
+ {
+ InspectorIface.get(L).tinit();
+ return 0;
+ },
+ },
+ {
+ "tterm",
+ [](lua_State* L)
+ {
+ InspectorIface.get(L).tterm();
+ return 0;
+ }
+ },
+ {
+ "likes",
+ [](lua_State* L)
+ {
+ auto& p = PacketIface.get(L, 1);
+ auto& self = InspectorIface.get(L);
+ bool result = self.likes(&p);
+ lua_pushboolean(L, result);
+ return 1;
+ }
+ },
{
"eval",
[](lua_State* L)
return 0;
}
},
+ // FIXIT-M: Add meta() method
+ // FIXIT-M: Add exec() method
{
"get_buf_from_key",
[](lua_State* L)
{
- bool result;
+ Lua::Args args(L);
auto& p = PacketIface.get(L, 2);
auto& rb = RawBufferIface.get(L, 3);
- auto& self = InspectorIface.get(L);
- {
- auto key = Lua::Stack<std::string>::get(L, 1);
- result = get_buf(self, key.c_str(), p, rb);
- }
+ auto& self = InspectorIface.get(L);
+ bool result = get_buf(self, args[1].check_string(), p, rb);
lua_pushboolean(L, result);
return 1;
"get_buf_from_id",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- int id = arg.check_int(1);
+ int id = args[1].check_int();
auto& p = PacketIface.get(L, 2);
auto& rb = RawBufferIface.get(L, 3);
auto& self = InspectorIface.get(L);
bool result = get_buf(self, id, p, rb);
-
lua_pushboolean(L, result);
return 1;
"get_buf_from_type",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- int type = arg.check_int(1);
+ auto type = static_cast<InspectionBuffer::Type>(args[1].check_int());
auto& p = PacketIface.get(L, 2);
auto& rb = RawBufferIface.get(L, 3);
auto& self = InspectorIface.get(L);
- bool result = get_buf(
- self,
- static_cast<InspectionBuffer::Type>(type),
- p, rb
- );
-
+ bool result = get_buf(self, type, p, rb);
lua_pushboolean(L, result);
return 1;
"get_splitter",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- bool to_server = arg.check_boolean(1);
+ bool to_server = args[1].check_bool();
auto& self = InspectorIface.get(L);
auto** sp = StreamSplitterIface.allocate(L);
#include <luajit-2.0/lua.hpp>
#include "framework/ips_option.h"
+#include "lua/lua_stack.h"
#include "pp_packet_iface.h"
#include "pp_cursor_iface.h"
static const luaL_Reg methods[] =
{
+ {
+ "hash",
+ [](lua_State* L)
+ {
+ uint32_t result = IpsOptionIface.get(L).hash();
+ Lua::Stack<uint32_t>::push(L, result);
+ return 1;
+ }
+ },
+ {
+ "is_relative",
+ [](lua_State* L)
+ {
+ bool result = IpsOptionIface.get(L).is_relative();
+ lua_pushboolean(L, result);
+ return 1;
+ }
+ },
+ {
+ "fp_research",
+ [](lua_State* L)
+ {
+ bool result = IpsOptionIface.get(L).fp_research();
+ lua_pushboolean(L, result);
+ return 1;
+ }
+ },
+ {
+ "get_cursor_type",
+ [](lua_State* L)
+ {
+ CursorActionType cat = IpsOptionIface.get(L).get_cursor_type();
+ Lua::Stack<unsigned>::push(L, static_cast<unsigned>(cat));
+ return 1;
+ }
+ },
{
"eval",
[](lua_State* L)
"open",
[](lua_State* L)
{
- auto& self = LoggerIface.get(L);
- self.open();
+ LoggerIface.get(L).open();
return 0;
}
},
"close",
[](lua_State* L)
{
- auto& self = LoggerIface.get(L);
- self.close();
+ LoggerIface.get(L).close();
return 0;
}
},
"reset",
[](lua_State* L)
{
- auto& self = LoggerIface.get(L);
- self.reset();
+ LoggerIface.get(L).reset();
return 0;
}
},
"alert",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& p = PacketIface.get(L, 1);
- const char* msg = arg.check_string(2);
auto& e = EventIface.get(L, 3);
auto& self = LoggerIface.get(L);
+ const char* msg = args[2].check_string();
self.alert(&p, msg, &e);
return 0;
"log",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& p = PacketIface.get(L, 1);
- const char* msg = arg.check_string(2);
auto& e = EventIface.get(L, 3);
auto& self = LoggerIface.get(L);
+ const char* msg = args[2].check_string();
self.log(&p, msg, &e);
return 0;
#include "pp_packet_iface.h"
+#include <string.h>
#include <luajit-2.0/lua.hpp>
#include "lua/lua_arg.h"
#include "pp_raw_buffer_iface.h"
#include "pp_daq_pkthdr_iface.h"
+static void set_fields(lua_State* L, int tindex, Packet& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.get_field("packet_flags", self.packet_flags);
+ table.get_field("xtradata_mask", self.xtradata_mask);
+ table.get_field("proto_bits", self.proto_bits);
+ table.get_field(
+ "application_protocol_ordinal",
+ self.application_protocol_ordinal
+ );
+
+ table.get_field("alt_dsize", self.alt_dsize);
+ table.get_field("num_layers", self.num_layers);
+ table.get_field("iplist_id", self.iplist_id);
+ table.get_field("user_policy_id", self.user_policy_id);
+ table.get_field("ps_proto", self.ps_proto);
+}
+
+static void get_fields(lua_State* L, int tindex, Packet& self)
+{
+ Lua::Table table(L, tindex);
+
+ table.set_field("packet_flags", self.packet_flags);
+ table.set_field("xtradata_mask", self.xtradata_mask);
+ table.set_field("proto_bits", self.proto_bits);
+ table.set_field(
+ "application_protocol_ordinal",
+ self.application_protocol_ordinal
+ );
+
+ table.set_field("alt_dsize", self.alt_dsize);
+ table.set_field("num_layers", self.num_layers);
+ table.set_field("iplist_id", self.iplist_id);
+ table.set_field("user_policy_id", self.user_policy_id);
+ table.set_field("ps_proto", self.ps_proto);
+}
+
+static void set(lua_State* L, Packet& self, Lua::Args& args, int start)
+{
+ for ( int i = start; i <= args.count; i++ )
+ {
+ if ( args[i].is_string() )
+ {
+ size_t len = 0;
+ const char* s = args[i].check_string(len);
+ auto& rb = RawBufferIface.create(L, s, len);
+ self.pkt = get_data(rb);
+ Lua::add_ref(L, &self, "pkt", lua_gettop(L));
+ lua_pop(L, 1);
+ }
+ else if ( args[i].is_size() )
+ {
+ size_t sz = args[i].check_size();
+ auto& rb = RawBufferIface.create(L, sz, '\0');
+ self.pkt = get_data(rb);
+ Lua::add_ref(L, &self, "pkt", lua_gettop(L));
+ lua_pop(L, 1);
+ }
+ else if ( args[i].is_table() )
+ {
+ args[i].check_table(set_fields, self);
+ }
+ else if ( RawBufferIface.is(L, i) )
+ {
+ self.pkt = get_data(RawBufferIface.get(L, i));
+ Lua::add_ref(L, &self, "pkt", i);
+ }
+ else if ( DAQHeaderIface.is(L, i) )
+ {
+ self.pkth = &DAQHeaderIface.get(L, i);
+ Lua::add_ref(L, &self, "pkth", i);
+ }
+ else
+ {
+ luaL_argerror(L, i,
+ "expected string or unsigned or table or RawBuffer or DAQHeader");
+ }
+ }
+}
+
static const luaL_Reg methods[] =
{
{
"new",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = PacketIface.create(L);
+ self.reset();
- // (Optional) first argument is raw packet
- if ( arg.count )
- {
- auto& rb = RawBufferIface.get(L, 1);
- self.pkt = reinterpret_cast<const uint8_t*>(rb.data());
- }
-
- // (Optional) second argument is daq header
- if ( arg.count > 1 )
- self.pkth = &DAQHeaderIface.get(L, 2);
+ set(L, self, args, 1);
return 1;
}
"set_decode_data",
[](lua_State* L)
{
- auto& self = PacketIface.get(L, 1);
- auto& dd = DecodeDataIface.get(L, 2);
-
- self.ptrs = dd;
-
+ PacketIface.get(L, 1).ptrs = DecodeDataIface.get(L, 2);
return 0;
}
},
"set_data",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = PacketIface.get(L, 1);
- size_t offset = arg.check_size(2);
- size_t size = arg.check_size(3);
+ size_t offset = args[2].check_size();
+ size_t size = args[3].check_size();
self.data = self.pkt + offset;
self.dsize = size;
auto& flow = FlowIface.get(L, 2);
self.flow = &flow;
+ Lua::add_ref(L, &self, "flow", 2);
return 0;
}
{
"get",
[](lua_State* L)
- {
- auto& self = PacketIface.get(L);
- lua_newtable(L);
-
- Lua::Table table(L, lua_gettop(L));
-
- table.set_field("packet_flags", self.packet_flags);
- table.set_field("xtradata_mask", self.xtradata_mask);
- table.set_field("proto_bits", self.proto_bits);
- table.set_field(
- "application_protocol_ordinal",
- self.application_protocol_ordinal
- );
-
- table.set_field("alt_dsize", self.alt_dsize);
- table.set_field("num_layers", self.num_layers);
- table.set_field("iplist_id", self.iplist_id);
- table.set_field("user_policy_id", self.user_policy_id);
- table.set_field("ps_proto", self.ps_proto);
-
- return 1;
- }
+ { return PacketIface.default_getter(L, get_fields); }
},
{
"set",
[](lua_State* L)
{
- Lua::Arg arg(L);
-
- auto& self = PacketIface.get(L, 1);
-
- arg.check_table(2);
+ Lua::Args args(L);
- Lua::Table table(L, 2);
- table.get_field("packet_flags", self.packet_flags);
- table.get_field("xtradata_mask", self.xtradata_mask);
- table.get_field("proto_bits", self.proto_bits);
- table.get_field(
- "application_protocol_ordinal",
- self.application_protocol_ordinal
- );
-
- table.get_field("alt_dsize", self.alt_dsize);
- table.get_field("num_layers", self.num_layers);
- table.get_field("iplist_id", self.iplist_id);
- table.get_field("user_policy_id", self.user_policy_id);
- table.get_field("ps_proto", self.ps_proto);
-
- return 0;
- }
- },
- {
- "set_pkt",
- [](lua_State* L)
- {
auto& self = PacketIface.get(L, 1);
- auto& rb = RawBufferIface.get(L, 2);
- self.pkt = reinterpret_cast<const uint8_t*>(rb.data());
-
- return 0;
- }
- },
- {
- "set_daq",
- [](lua_State* L)
- {
- auto& self = PacketIface.get(L, 1);
- auto& daq = DAQHeaderIface.get(L, 2);
-
- self.pkth = &daq;
+ set(L, self, args, 2);
return 0;
}
{
"__tostring",
[](lua_State* L)
- {
- auto& self = PacketIface.get(L);
- lua_pushfstring(L, "%s@%p", PacketIface.name, &self);
- return 1;
- }
+ { return PacketIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- PacketIface.destroy(L);
- return 0;
- }
+ { return PacketIface.default_gc(L); }
},
{ nullptr, nullptr }
};
static int init_from_string(lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
size_t len = 0;
- const char* s = arg.check_string(1, &len);
- size_t size = arg.opt_size(2, len);
+ const char* s = args[1].check_string(len);
+ size_t size = args[2].opt_size(len);
// instantiate and adjust size if necessary
RawBufferIface.create(L, s, len).resize(size, '\0');
static int init_from_size(lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- size_t size = arg.opt_size(1);
+ size_t size = args[1].opt_size();
RawBufferIface.create(L, size, '\0');
"new",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
- if ( arg.is_string(1) )
+ if ( args[1].is_string() )
return init_from_string(L);
return init_from_size(L);
{
auto& self = RawBufferIface.get(L);
lua_pushinteger(L, self.size());
-
return 1;
}
},
"resize",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = RawBufferIface.get(L, 1);
- size_t new_size = arg.check_size(2);
+ size_t new_size = args[2].check_size();
self.resize(new_size, '\0');
"write",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = RawBufferIface.get(L, 1);
size_t len = 0;
- const char* s = arg.check_string(2, &len);
- size_t offset = arg.opt_size(3);
+ const char* s = args[2].check_string(len);
+ size_t offset = args[3].opt_size();
size_t required = offset + len;
if ( self.size() < required )
"read",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = RawBufferIface.get(L, 1);
- if ( arg.count > 2 )
+ if ( args.count > 2 )
{
- size_t start = arg.check_size(2, self.size());
- size_t end = arg.check_size(3, start, self.size());
+ size_t start = args[2].check_size(self.size());
+ size_t end = args[3].check_size(start, self.size());
lua_pushlstring(L, self.data() + start, end - start);
}
else
{
- size_t end = arg.opt_size(2, self.size(), self.size());
+ size_t end = args[2].opt_size(self.size(), self.size());
lua_pushlstring(L, self.data(), end);
}
[](lua_State* L)
{
auto& self = RawBufferIface.get(L);
- lua_pushfstring(L, "%s@%p", RawBufferIface.name, &self);
+ lua_pushlstring(L, self.data(), self.size());
return 1;
}
},
{
"__gc",
[](lua_State* L)
- {
- RawBufferIface.destroy(L);
- return 0;
- }
+ { return RawBufferIface.default_gc(L); }
},
{ nullptr, nullptr }
};
using RawBuffer = std::string;
+static inline const uint8_t* get_data(RawBuffer& rb)
+{ return reinterpret_cast<const uint8_t*>(rb.data()); }
+
+static inline uint8_t* get_mutable_data(RawBuffer& rb)
+{ return const_cast<uint8_t*>(get_data(rb)); }
+
extern const struct Lua::TypeInterface<RawBuffer> RawBufferIface;
#endif
+++ /dev/null
-//--------------------------------------------------------------------------
-// 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_raw_data_iface.cc author Joel Cornett <jocornet@cisco.com>
-
-#include "pp_raw_data_iface.h"
-
-#include <luajit-2.0/lua.hpp>
-
-extern "C" {
-#include <daq.h>
-}
-
-#include "framework/codec.h"
-#include "pp_raw_buffer_iface.h"
-#include "pp_daq_pkthdr_iface.h"
-
-static const luaL_Reg methods[] =
-{
- {
- "new",
- [](lua_State* L)
- {
- auto& rb = RawBufferIface.get(L, 1);
- auto& daq = DAQHeaderIface.get(L, 2);
-
- RawDataIface.create(
- L, &daq, reinterpret_cast<const uint8_t*>(rb.data()));
-
- return 1;
- }
- },
- { nullptr, nullptr }
-};
-
-static const luaL_Reg metamethods[] =
-{
- {
- "__tostring",
- [](lua_State* L)
- {
- auto& self = RawDataIface.get(L);
- lua_pushfstring(L, "%s@%p", RawDataIface.name, &self);
-
- return 1;
- }
- },
- {
- "__gc",
- [](lua_State* L)
- {
- RawDataIface.destroy(L);
- return 0;
- }
- },
- { nullptr, nullptr }
-};
-
-const struct Lua::TypeInterface<RawData> RawDataIface =
-{
- "RawData",
- methods,
- metamethods
-};
"scan",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = StreamSplitterIface.get(L, 1);
auto& flow = FlowIface.get(L, 2);
auto& rb = RawBufferIface.get(L, 3);
- uint32_t len = arg.opt_size(4, rb.size(), rb.size());
- uint32_t flags = arg.opt_size(5);
+ uint32_t len = args[4].opt_size(rb.size(), rb.size());
+ uint32_t flags = args[5].opt_size();
uint32_t fp = 0;
- auto status = self.scan(
- &flow, reinterpret_cast<const uint8_t*>(rb.data()),
- len, flags, &fp
- );
+ auto status = self.scan(&flow, get_data(rb), len, flags, &fp);
stack_push<StreamSplitter::Status, unsigned>(L, status);
Lua::Stack<uint32_t>::push(L, fp);
"reassemble",
[](lua_State* L)
{
- Lua::Arg arg(L);
+ Lua::Args args(L);
auto& self = StreamSplitterIface.get(L, 1);
auto& flow = FlowIface.get(L, 2);
- unsigned total = arg.check_size(3);
- unsigned offset = arg.check_size(4);
+ unsigned total = args[3].check_size();
+ unsigned offset = args[4].check_size();
auto& rb = RawBufferIface.get(L, 5);
- unsigned len = arg.opt_size(6, rb.size());
- uint32_t flags = arg.opt_size(7);
+ unsigned len = args[6].opt_size(rb.size());
+ uint32_t flags = args[7].opt_size();
unsigned copied = 0;
- auto sb = self.reassemble(
- &flow, total, offset,
- reinterpret_cast<const uint8_t*>(rb.data()), len,
- flags, copied
- );
+ auto sb = self.reassemble(&flow, total, offset, get_data(rb), len,
+ flags, copied);
Lua::Stack<unsigned>::push(L, copied);
auto& flow = FlowIface.get(L, 2);
bool result = self.finish(&flow);
-
lua_pushboolean(L, result);
return 1;
{
"__tostring",
[](lua_State* L)
- {
- auto& self = StreamSplitterIface.get(L);
- lua_pushfstring(L, "%s@%p", StreamSplitterIface.name, &self);
-
- return 1;
- }
+ { return StreamSplitterIface.default_tostring(L); }
},
{
"__gc",
[](lua_State* L)
- {
- StreamSplitterIface.destroy(L);
- return 0;
- }
+ { return StreamSplitterIface.default_gc(L); }
},
{ nullptr, nullptr }
};
#include "pp_flow_iface.h"
#include "pp_packet_iface.h"
#include "pp_raw_buffer_iface.h"
-#include "pp_raw_data_iface.h"
class TestPiglet : public Piglet::BasePlugin
{
install(L, FlowIface);
install(L, PacketIface);
install(L, RawBufferIface);
- install(L, RawDataIface);
return false;
}
add_library(unit_tests STATIC
suite_decl.h
suite_list.h
+ lua_stack_test.cc
+ lua_test_common.h
sfip_test.cc
sfrf_test.cc
sfrt_test.cc
suite_list.h
libtest_a_SOURCES = \
+lua_stack_test.cc \
+lua_test_common.h \
sfip_test.cc \
sfrf_test.cc \
sfrt_test.cc \
--- /dev/null
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// lua_stack_test.cc author Joel Cornett <jocornet@cisco.com>
+
+#include <string>
+#include <luajit-2.0/lua.hpp>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+#include <check.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#include "lua_test_common.h"
+#include "lua/lua_stack.h"
+
+static lua_State* L = nullptr;
+
+static void LuaCleanup(void)
+{ l_end_lua_state(L); }
+
+static void LuaFixture(void)
+{ l_reset_lua_state(L); }
+
+START_TEST(test_signed)
+{
+ bool b;
+ int k;
+
+ // Test get
+ lua_pushinteger(L, 3);
+ {
+ auto r = Lua::Stack<int>::get(L, -1);
+ ck_assert_int_eq(r, 3);
+ }
+ lua_pop(L, 1);
+
+ // Test push
+ int j = 4;
+ Lua::Stack<int>::push(L, j);
+ {
+ lua_Integer l = lua_tointeger(L, -1);
+ ck_assert_int_eq(l, j);
+ }
+ lua_pop(L, 1);
+
+ // Test validate, no output
+ lua_pushinteger(L, 1);
+ {
+ b = Lua::Stack<int>::validate(L, -1);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate
+ k = 0;
+ lua_pushinteger(L, 5);
+ {
+ b = Lua::Stack<int>::validate(L, -1, k);
+ ck_assert_int_eq(k, 5);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate false
+ k = 7;
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<int>::validate(L, -1, k);
+ ck_assert_int_eq(k, 7);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+}
+END_TEST
+
+START_TEST(test_unsigned)
+{
+ bool b;
+ unsigned short k;
+
+ // Test get
+ lua_pushinteger(L, 3);
+ {
+ auto r = Lua::Stack<unsigned short>::get(L, -1);
+ ck_assert_int_eq(r, 3);
+ }
+ lua_pop(L, 1);
+
+ // Test push
+ unsigned short j = 4;
+ Lua::Stack<unsigned short>::push(L, j);
+ {
+ lua_Integer l = lua_tointeger(L, -1);
+ ck_assert_int_eq(l, j);
+ }
+ lua_pop(L, 1);
+
+ // Test validate, no output
+ lua_pushinteger(L, 1);
+ {
+ b = Lua::Stack<unsigned short>::validate(L, -1);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate
+ k = 0;
+ lua_pushinteger(L, 5);
+ {
+ b = Lua::Stack<unsigned short>::validate(L, -1, k);
+ ck_assert_int_eq(k, 5);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate false
+ k = 7;
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<unsigned short>::validate(L, -1, k);
+ ck_assert_int_eq(k, 7);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+ lua_pushinteger(L, -1);
+ {
+ b = Lua::Stack<unsigned short>::validate(L, -1, k);
+ ck_assert_int_eq(k, 7);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+}
+END_TEST
+
+START_TEST(test_cstring)
+{
+ bool b;
+ const char* s;
+ size_t len;
+
+ // Test type
+ ck_assert_int_eq(Lua::Stack<const char*>::type(), LUA_TSTRING);
+
+ // Test get
+ s = nullptr;
+ lua_pushstring(L, "foo");
+ {
+ s = Lua::Stack<const char*>::get(L, -1);
+ ck_assert_str_eq(s, "foo");
+ }
+ lua_pop(L, 1);
+
+ // Test get w/length
+ s = nullptr;
+ len = 0;
+ lua_pushlstring(L, "f\0b", 3);
+ {
+ s = Lua::Stack<const char*>::get(L, -1, len);
+ ck_assert_uint_eq(len, 3);
+ l_assert_strn_eq(s, "f\0b", len);
+ }
+ lua_pop(L, 1);
+
+ // Test push
+ s = "foo";
+ Lua::Stack<const char*>::push(L, s);
+ {
+ s = lua_tostring(L, -1);
+ ck_assert_str_eq(s, "foo");
+ }
+ lua_pop(L, 1);
+
+ len = 0;
+ s = "f\0o";
+ Lua::Stack<const char*>::push(L, s);
+ {
+ s = lua_tolstring(L, -1, &len);
+ ck_assert_str_eq(s, "f");
+ ck_assert_uint_eq(len, 1);
+ }
+ lua_pop(L, 1);
+
+ // Test push w/length
+ len = 0;
+ s = "f\0b";
+ Lua::Stack<const char*>::push(L, s, 3);
+ {
+ s = lua_tolstring(L, -1, &len);
+ ck_assert_uint_eq(len, 3);
+ l_assert_strn_eq(s, "f\0b", len);
+ }
+ lua_pop(L, 1);
+
+ // Test validate, no output
+ lua_pushstring(L, "foo");
+ {
+ b = Lua::Stack<const char*>::validate(L, -1);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate
+ len = 0;
+ s = "foo";
+ lua_pushstring(L, s);
+ {
+ s = nullptr;
+ b = Lua::Stack<const char*>::validate(L, -1, s);
+ ck_assert(b);
+ ck_assert_str_eq(s, "foo");
+ }
+ lua_pop(L, 1);
+
+ // Test validate w/length
+ len = 0;
+ s = "f\0b";
+ lua_pushlstring(L, s, 3);
+ {
+ s = nullptr;
+ b = Lua::Stack<const char*>::validate(L, -1, s, len);
+ ck_assert(b);
+ l_assert_strn_eq(s, "f\0b", 3);
+ }
+ lua_pop(L, 1);
+
+ // Test invalid
+ s = nullptr;
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<const char*>::validate(L, -1, s);
+ ck_assert(!s);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+
+ s = nullptr;
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<const char*>::validate(L, -1, s, len);
+ ck_assert(!s);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+}
+END_TEST
+
+START_TEST(test_string)
+{
+ bool b;
+ const char* cs;
+ std::string s;
+ size_t len;
+
+ // Test type
+ ck_assert_int_eq(Lua::Stack<std::string>::type(), LUA_TSTRING);
+
+ // Test get
+ lua_pushstring(L, "foo");
+ {
+ s = Lua::Stack<std::string>::get(L, -1);
+ ck_assert(s == "foo");
+ }
+ lua_pop(L, 1);
+
+ // Test get w/zeros
+ lua_pushlstring(L, "f\0b", 3);
+ {
+ s = Lua::Stack<std::string>::get(L, -1);
+ ck_assert(s.length() == 3);
+ l_assert_strn_eq(s.c_str(), "f\0b", 3);
+ }
+ lua_pop(L, 1);
+
+ // Test push
+ s = "foo";
+ Lua::Stack<std::string>::push(L, s);
+ {
+ cs = lua_tostring(L, -1);
+ ck_assert_str_eq(cs, "foo");
+ }
+ lua_pop(L, 1);
+
+ len = 0;
+ s.assign("f\0b", 3);
+ Lua::Stack<std::string>::push(L, s);
+ {
+ cs = lua_tolstring(L, -1, &len);
+ ck_assert_uint_eq(len, 3);
+ l_assert_strn_eq(cs, "f\0b", len);
+ }
+ lua_pop(L, 1);
+
+ // Test validate, no output
+ lua_pushstring(L, "foo");
+ {
+ b = Lua::Stack<std::string>::validate(L, -1);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate
+ lua_pushstring(L, "foo");
+ {
+ b = Lua::Stack<std::string>::validate(L, -1, s);
+ ck_assert(b);
+ ck_assert(s == "foo");
+ }
+ lua_pop(L, 1);
+
+ lua_pushlstring(L, "f\0o", 3);
+ {
+ b = Lua::Stack<std::string>::validate(L, -1, s);
+ ck_assert(b);
+ ck_assert_uint_eq(s.length(), 3);
+ l_assert_strn_eq(s.c_str(), "f\0o", 3);
+ }
+ lua_pop(L, 1);
+
+ // Test invalid
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<std::string>::validate(L, -1, s);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+}
+END_TEST
+
+START_TEST(test_bool)
+{
+ bool b, v;
+
+ // Test get
+ lua_pushboolean(L, true);
+ {
+ v = Lua::Stack<bool>::get(L, -1);
+ ck_assert(v);
+ }
+ lua_pop(L, 1);
+
+ // Test push
+ Lua::Stack<bool>::push(L, true);
+ {
+ v = lua_toboolean(L, -1);
+ ck_assert(v);
+ }
+ lua_pop(L, 1);
+
+ // Test validate, no output
+ lua_pushboolean(L, true);
+ {
+ b = Lua::Stack<bool>::validate(L, -1);
+ ck_assert(b);
+ }
+ lua_pop(L, 1);
+
+ // Test validate
+ lua_pushboolean(L, true);
+ {
+ b = Lua::Stack<bool>::validate(L, -1, v);
+ ck_assert(b);
+ ck_assert(v);
+ }
+ lua_pop(L, 1);
+
+ // Test invalid
+ lua_pushnil(L);
+ {
+ b = Lua::Stack<bool>::validate(L, -1, v);
+ ck_assert(!b);
+ }
+ lua_pop(L, 1);
+}
+END_TEST
+
+Suite* TEST_SUITE_lua_stack(void)
+{
+ Suite* ps = suite_create("lua_stack");
+
+ TCase* tc = tcase_create("lua_stack");
+ tcase_add_unchecked_fixture(tc, LuaFixture, LuaCleanup);
+ tcase_add_test(tc, test_signed);
+ tcase_add_test(tc, test_unsigned);
+ tcase_add_test(tc, test_cstring);
+ tcase_add_test(tc, test_string);
+ tcase_add_test(tc, test_bool);
+ suite_add_tcase(ps, tc);
+
+ return ps;
+}
--- /dev/null
+#ifndef LUA_TEST_COMMON_H
+#define LUA_TEST_COMMON_H
+
+#include <utility>
+#include <string.h>
+#include <luajit-2.0/lua.hpp>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+#include <check.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+static inline void l_assert_strn_eq(const char* a, const char* b, size_t n)
+{ ck_assert(!strncmp(a, b, n)); }
+
+static inline void l_end_lua_state(lua_State*& L_ptr)
+{
+ if ( L_ptr )
+ {
+ lua_close(L_ptr);
+ L_ptr = nullptr;
+ }
+}
+
+static inline void l_reset_lua_state(lua_State*& L_ptr)
+{
+ l_end_lua_state(L_ptr);
+ L_ptr = luaL_newstate();
+ luaL_openlibs(L_ptr);
+}
+
+template<typename T, size_t N>
+static inline constexpr size_t sizeofArray(T (&)[N])
+{ return N; }
+
+#endif