require("bin") --[[ The NDR wire protocol, also referred to as transfer syntax, enables RPC calls to traverse the network. The wire protocol defines the wire representation of an RPC call, such as the order in which data members are marshaled, alignment of data on the wire, additional information included with the data, and other issues. References: [1] "DCE 1.1: Remote Procedure Call", The Open Group [2] "[MS-RPCE]: Remote Procedure Call Protocol Extensions", MSDN [3] "RPC NDR engine", http://msdn.microsoft.com/en-us/library/ms764233%28v=VS.85%29.aspx [4] "PyMSRPC", http://code.google.com/p/pymsrpc/ --]] --####################################################################-- --# 1) NDR class --####################################################################-- local ptr_unique_flag = 1 local ptr_full_flag = 2 local Pointers = {} function Pointers:new() self.pointers = {} self.next_ref_id = 1 return self end function Pointers:getReferentId(lua_ptr, ptr_type_flag) if(lua_ptr == nil) then return 0 end if(self.pointers[lua_ptr] == nil) then self.pointers[lua_ptr] = self.next_ref_id self.next_ref_id = self.next_ref_id + 1 else if(ptr_type_flag == ptr_unique_flag) then return 0 end end return self.pointers[lua_ptr] end NDR = { BOOL_TRUE = 0x01, BOOL_FALSE = 0x00 } function NDR:new() local o = {} setmetatable(o, self) self.__index = self --initalize the NDR object o.push_blob = "" --initialize the empty push_blob o.pull_blob = "" --initialize the empty pull blob o.pull_blob_pos = 1 --for keeping track the NEXT pull position o.char_fmt = "ascii"--ascii character format o.int_bo = "le" --little endian o.fp_rep = "ieee" --IEEE floating point representation Pointers:new() o.aliases = {} return o end function NDR:push(blob) self.push_blob = self.push_blob .. blob return string.len(blob) end function NDR:pull(blob_size) local ndr_error = NDR_Error:new() if(self.pull_blob_pos + blob_size - 1 > #self.pull_blob) then local error_msg = string.format( "[NDR:pull] %s (needed %dB but have only %dB left)", NDR_Error.ERRORS["NDR_TRUNC"], blob_size, #self.pull_blob - self.pull_blob_pos + 1 ) ndr_error:Activate(error_msg) return "", ndr_error end local blob = string.sub( self.pull_blob, self.pull_blob_pos, self.pull_blob_pos + blob_size - 1) self.pull_blob_pos = self.pull_blob_pos + blob_size return blob, ndr_error end function NDR:setAlias(full_ptr) self.aliases[full_ptr:getReferentId()] = true end function NDR:isAlias(full_ptr) return self.aliases[full_ptr:getReferentId()] end function NDR:getByteOrderOp() return self.int_bo == "le" and "<" or ">" end ---Marshall a NDR boolean type and push it onto a ndr stack. -- @see [1] Chapter: "14.2.3 Booleans" function NDR:push_bool(ndr_bool) return self, ndr_bool:push(self) end function NDR:pull_bool(ndr_bool) return self, ndr_bool:pull(self) end ---Marshall a NDR char type and push it onto a ndr stack. -- @see [1] Chapter: "14.2.4 Characters" function NDR:push_char(ndr_char) return self, ndr_char:push(self) end function NDR:pull_char(ndr_char) return self, ndr_char:pull(self) end ---Marshall a NDR small type, both signed and unsigned, and push it onto a ndr stack. -- @ndr_desc An 8-bit integer, represented in the octet stream as 1 octet. -- @see [1] Chapter: "14.2.5 Integers and Enumerated Types" function NDR:push_unsigned_small(ndr_unsigned_small) return self, ndr_unsigned_small:push(self) end function NDR:pull_unsigned_small(ndr_unsigned_small) return self, ndr_unsigned_small:pull(self) end function NDR:push_signed_small(ndr_signed_small) return self, ndr_signed_small:push(self) end function NDR:pull_signed_small(ndr_signed_small) return self, ndr_signed_small:pull(self) end ---Marshall a NDR short type, both signed and unsigned, and push it onto a ndr stack. -- @ndr_desc An 16-bit integer, represented in the octet stream as 2 octets. -- @see [1] Chapter: "14.2.5 Integers and Enumerated Types" function NDR:push_unsigned_short(ndr_unsigned_short) return self, ndr_unsigned_short:push(self) end function NDR:pull_unsigned_short(ndr_unsigned_short) return self, ndr_unsigned_short:pull(self) end function NDR:push_signed_short(ndr_signed_short) return self, ndr_signed_short:push(self) end function NDR:pull_signed_short(ndr_signed_short) return self, ndr_signed_short:pull(self) end ---Marshall a NDR long type, both signed and unsigned, and push it onto a ndr stack. -- @ndr_desc An 32-bit integer, represented in the octet stream as 4 octets. -- @see [1] Chapter: "14.2.5 Integers and Enumerated Types" function NDR:push_unsigned_long(ndr_unsigned_long) ndr_unsigned_long:push(self) return self end function NDR:pull_unsigned_long(ndr_unsigned_long) return self, ndr_unsigned_long:pull(self) end function NDR:push_signed_long(ndr_signed_long) return self, ndr_signed_long:push(self) end function NDR:pull_signed_long(ndr_signed_long) return self, ndr_signed_long:pull(self) end ---Marshall a NDR hyper type, both signed and unsigned, and push it onto a ndr stack. -- @ndr_desc An 64-bit integer, represented in the octet stream as 8 octets. -- @see [1] Chapter: "14.2.5 Integers and Enumerated Types" function NDR:push_unsigned_hyper(ndr_unsigned_hyper) return self, ndr_unsigned_hyper:push(self) end function NDR:pull_unsigned_hyper(ndr_unsigned_hyper) return self, ndr_unsigned_hyper:pull(self) end function NDR:push_signed_hyper(ndr_signed_hyper) return self, ndr_signed_hyper:push(self) end function NDR:pull_signed_hyper(ndr_signed_hyper) return self, ndr_signed_hyper:pull(self) end ---Marshall a NDR enum type and push it onto a ndr stack. -- @ndr_desc NDR represents enumerated types as signed short integers (2 octets). -- @see [1] Chapter: "14.2.5 Integers and Enumerated Types" function NDR:push_enum(ndr_enum) return self, ndr_enum:push(self) end function NDR:pull_enum(ndr_enum) return self, ndr_enum:pull(self) end ---Marshall a NDR uninterpreted octet type and push it onto a ndr stack. -- @ndr_desc NDR defines an uninterpreted octet data type for which no internal --format is defined and on which no format conversions are made. -- @see [1] Chapter: "14.2.7 Uninterpreted Octets" function NDR:push_octet(ndr_octet) return self, ndr_octet:push(self) end function NDR:pull_octet(ndr_octet) return self, ndr_octet:pull(self) end ---Marshall a NDR n-dimensional fixed array type and push it onto a ndr stack. -- @ndr_desc NDR represents a fixed array as an ordered sequence of representations --of the array elements. -- @see [1] Chapter: "14.3.3.1 Uni-dimensional Fixed Arrays" function NDR:push_fixed_arr(ndr_fixed_arr) return self, ndr_fixed_arr:push(self) end function NDR:pull_fixed_arr(ndr_fixed_arr) return self, ndr_fixed_arr:pull(self) end --####################################################################-- --# NDR Debug Class --####################################################################-- NDR_Debug = { nest_lvl = 0, indent = function(self) self.nest_lvl = self.nest_lvl + 1 end, deindent = function(self) self.nest_lvl = self.nest_lvl - 1 end, debug_lvl = 2, debug = function(self, msg, nest_lvl, dbg_lvl) dbg_lvl = dbg_lvl or self.debug_lvl nest_lvl = nest_lvl or self.nest_lvl local nest_prefix = string.rep("\t", nest_lvl) local dbg_msg = string.format("[NDR] %s%s", nest_prefix, msg) stdnse.print_debug( dbg_lvl, dbg_msg ) end, } --####################################################################-- --# NDR Error Class --####################################################################-- NDR_Error = { new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self return o end, active = false, Activate = function(self, msg) self:push(msg) self.active = true end, isActive = function(self) return self.active end, msgs = {}, push = function(self, msg) self.msgs[#self.msgs+1] = msg end, pull = function(self) local msg = self.msgs[#self.msgs] self.msgs[#self.msgs] = nil return msg end, toString = function(self) local error_msg = "" for i=#self.msgs,1,-1 do error_msg = error_msg .. string.format("\t[%d.] %s\t", i, self.msgs[i]) end return error_msg end, ERRORS = { ["NDR_NULL"] = "NDR object is not instantiated", ["NDR_TRUNC"] = "NDR pull stack is truncated", ["NDR_PULL"] = "Failed to pull the data", }, } --####################################################################-- --# 2) NDR types class --####################################################################-- --[[ To elegantly handle both the primitive and the constructed NDR types we must design a class that will represent each of these types. Every NDR:push_xxx() receives an object of the corresponding class, and in such way we emulate the types not already present in Lua such as various pointer types, arrays, structures... So for example: NDR:push_long() doesn't receive a Lua number, but a NDR_long object. The reason for this datatype wrapping is infact the attempt to battle the complexity of packing NDR constructed types. This will especially be useful when packing NDR structures, unions, multidimensional arrays and pointers. ]]-- ---A class that represents a base class for every NDR type. NDR_Type = { new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self return o end, name = "unnamed type", getName = function(self) return self.name end, setName = function(self, name) self.name = name end, --pure virtual methods! derived types must implement! getVal = function(self) end, setVal = function(self) end, setDefaultVal = function(self) end, getDefaltVal = function(self) end, setDefault = function(self) end, getAlignment = function(self) end, getSize = function(self) end, push = function(self) end, pull = function(self) end, getParts = function(self, conf_arrays, alignments, body, pointers) end, } --####################################################################-- --# 2) NDR primitive types --# @see [1] Chapter: "14.2 NDR Primitive Types" --####################################################################-- --[[ NDR primitives: * Boolean values * Characters (ASCII, EBCDIC) * Integers, signed and unsigned, four sizes (BIG, LITTLE ENDIAN) * Floating point numbers, two sizes (IEEE, VAX, CRAY, IBM) * Uninterpreted octets Important note for everyone figthing MSRPC and NDR out there. Don't forget the alignment...so @see [1] Chapter: "14.2.2 Alignment of Primitive Types". Suppose we are packing the NDR consisting of small, short ,long, small again and hyper. My first mistake was the following NDR package: [1B for small] [2B for short] [4B for long] [1B for small] [8B for hyper] --incorrect on the wire representation Which ended up in ndr_fault RPC error. What should be done is the following [0B pad for small alignment][1B for small] [1B pad for short alignment][2B for short] [0B for long alignment][4B for long] [0B for small alignment][1B for small] [7B for hyper alignment][8B for hyper] --correct on the wire representation Lua types vs. NDR primitive types It's important to note how Lua types are transformed into NDR types. For most of the NDR primitive types this is quite trivial because almost every NDR primitive type has an equal in Lua. The only exception to this is the ENUM type. The Lua enum type MUST be, for the sake of NDR, be represented as a table (enum) of string keys (enum names) and integer values which should designate their order in the enum. --]] NDR_PrimitiveType = NDR_Type:new { new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self return o end, lua_val_default = -1, getDefaultVal = function(self) return self.lua_val_default end, setDefaultVal = function(self, lua_val) self.lua_val_default = lua_val end, setDefault = function(self) self.lua_val = self.lua_val_default end, lua_val = -1, getVal = function(self) return self.lua_val end, setVal = function(self, lua_val) self.lua_val = lua_val or self.lua_val_default end, alignment = -1, getAlignment = function(self) return self.alignment end, setAlignment = function(self, alignment) self.alignment = alignment end, size = -1, getSize = function(self) return self.size end, setSize = function(self, size) self.size = size end, pack_op = "undefined op", getPackingOp = function(self) return self.pack_op end, setPackingOp = function(self, pack_op) self.pack_op = pack_op end, unpack_op = "undefined op", getUnPackingOp = function(self) return self.unpack_op end, setUnPackingOp = function(self, unpack_op) self.unpack_op = unpack_op end, marshall = function(self, lua_val, byte_order) return bin.pack( byte_order .. self:getPackingOp(), lua_val) end, push = function(self, ndr) local push_size = 0 local ndr_error = NDR_Error:new() if(ndr == nil) then local error_msg = string.format( "[NDR_PrimitiveType:push] %s (%s)", NDR_Error.ERRORS["NDR_NULL"], self:getName() ) ndr_error:Activate(error_msg) return 0, ndr_error end local lua_val = self:getVal() local blob blob = self:marshall(lua_val, ndr:getByteOrderOp()) local pad pad = get_pad(ndr.push_blob, self:getAlignment()) ndr:push(pad .. blob) --debug local debug_msg debug_msg = string.format( "[%s][push] pad=%dB,size=%dB,val=0x%x", self:getName(), #pad, #blob, (lua_val == true ) and NDR.BOOL_TRUE or lua_val == false and NDR.BOOL_FALSE or lua_val ) NDR_Debug:debug(debug_msg) push_size = #pad + #blob return push_size, ndr_error end, unmarshall = function(self, blob, byte_order) return bin.unpack( byte_order .. self:getUnPackingOp(), blob) end, pull = function(self, ndr) local pull_size = 0 local ndr_error = NDR_Error:new() if(ndr == nil) then local error_msg = string.format( "[NDR_PrimitiveType:pull] %s (%s)", NDR_Error.ERRORS["NDR_NULL"], self:getName() ) ndr_error:Activate(error_msg) return 0, ndr_error end local pad_size = string.len( get_pad( string.sub(ndr.pull_blob, 1, ndr.pull_blob_pos - 1), self:getAlignment())) local pad, ndr_error = ndr:pull(pad_size) if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_PrimitiveType:pull] %s (%s)", NDR_Error.ERRORS["NDR_PULL"], self:getName() ) ndr_error:Activate(error_msg) return 0, ndr_error end local blob, ndr_error = ndr:pull(self:getSize()) if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_PrimitiveType:pull] %s (%s)", NDR_Error.ERRORS["NDR_PULL"], self:getName() ) ndr_error:Activate(error_msg) return 0, ndr_error end local pos,lua_val pos, lua_val = self:unmarshall(blob, ndr:getByteOrderOp()) self:setVal(lua_val) --debug local debug_msg debug_msg = string.format( "[%s][pull] pad=%dB,size=%dB,val=0x%x", self:getName(), #pad, #blob, (lua_val == true ) and NDR.BOOL_TRUE or lua_val == false and NDR.BOOL_FALSE or lua_val ) NDR_Debug:debug(debug_msg) pull_size = #pad + #blob return pull_size, ndr_error end, getParts = function(self, conf_arrays, alignments, body, pointers) table.insert(alignments, self:getAlignment()) table.insert(body, self) end, } NDR_Bool = NDR_PrimitiveType:new { new = function(self, lua_bool, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(false) o:setVal(lua_bool) o:setAlignment(1) o:setSize(1) o:setPackingOp("C") o:setUnPackingOp("C") o:setName("bool") return o end, marshall = function(self, lua_bool, byte_order) local val = -1 if(lua_bool == false) then val = NDR.BOOL_FALSE else val = NDR.BOOL_TRUE end return bin.pack( byte_order .. self:getPackingOp(), val) end, unmarshall = function(self, blob, byte_order) local pos, val = bin.unpack( byte_order .. self:getUnPackingOp(), blob) local lua_bool = false if(val == NDR.BOOL_FALSE) then lua_bool = false else lua_bool = true end return pos, lua_bool end, } NDR_Char = NDR_PrimitiveType:new { new = function(self, lua_char, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaa) o:setVal(lua_char) o:setAlignment(1) o:setSize(1) o:setPackingOp("C") o:setUnPackingOp("C") o:setName("char") return o end } NDR_UnsignedSmall = NDR_PrimitiveType:new { new = function(self, lua_unsigned_small, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaa) o:setVal(lua_unsigned_small) o:setAlignment(1) o:setSize(1) o:setPackingOp("C") o:setUnPackingOp("C") o:setName("unsigned small") return o end } NDR_SignedSmall = NDR_PrimitiveType:new { new = function(self, lua_signed_small, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaa) o:setVal(lua_signed_small) o:setAlignment(1) o:setSize(1) o:setPackingOp("c") o:setUnPackingOp("c") o:setName("signed small") return o end } NDR_UnsignedShort = NDR_PrimitiveType:new { new = function(self, lua_unsigned_short, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaabb) o:setVal(lua_unsigned_short) o:setAlignment(2) o:setSize(2) o:setPackingOp("S") o:setUnPackingOp("S") o:setName("unsigned short") return o end } NDR_SignedShort = NDR_PrimitiveType:new { new = function(self, lua_signed_short, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaabb) o:setVal(lua_signed_short) o:setAlignment(2) o:setSize(2) o:setPackingOp("s") o:setUnPackingOp("s") o:setName("signed short") return o end } NDR_UnsignedLong = NDR_PrimitiveType:new { new = function(self, lua_unsigned_long, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaabbccdd) o:setVal(lua_unsigned_long) o:setAlignment(4) o:setSize(4) o:setPackingOp("I") o:setUnPackingOp("I") o:setName("unsigned long") return o end } NDR_SignedLong = NDR_PrimitiveType:new { new = function(self, lua_signed_long, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaabbccdd) o:setVal(lua_signed_long) o:setAlignment(4) o:setSize(4) o:setPackingOp("i") o:setUnPackingOp("i") o:setName("signed long") return o end } NDR_UnsignedHyper = NDR_PrimitiveType:new { new = function(self, lua_unsigned_hyper, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xdeadbeef) o:setVal(lua_unsigned_hyper) o:setAlignment(8) o:setSize(8) o:setPackingOp("L") o:setUnPackingOp("L") o:setName("unsigned hyper") return o end } NDR_SignedHyper = NDR_PrimitiveType:new { new = function(self, lua_signed_hyper, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xdeadbeef) o:setVal(lua_signed_hyper) o:setAlignment(8) o:setSize(8) o:setPackingOp("l") o:setUnPackingOp("l") o:setName("signed hyper") return o end } NDR_Enum = NDR_SignedShort:new( nil, { new = function(self, lua_enum, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setVal(lua_enum) o:setName("enum @signed short") return o end } ) NDR_Octet = NDR_PrimitiveType:new { new = function(self, lua_octet, o) o = o or {} setmetatable(o, self) self.__index = self --set o:setDefaultVal(0xaa) o:setVal(lua_octet) o:setAlignment(1) o:setSize(1) o:setPackingOp("C") o:setUnPackingOp("C") o:setName("octet") return o end } --####################################################################-- --# 2) NDR constructed types --# @see [1] Chapter: "14.3 NDR Constructed Types" --####################################################################-- --[[ NDR constructed types: * Arrays * Strings * Structures * Unions * Variant structures -- WTH is a variant structure...perhaps if it contains a variant array? * Pipes * Pointers NDR represents every NDR constructed type as a sequence of NDR primitive values. All NDR constructed data types are integer multiples of octets in length. As with primitive types, the greatest fuzz you can encounter while implementing NDR library is caused by alignment issues (and deferral issues :|)... so @see [1] Chapter: "14.3.2 Alignment of Constructed Types" and @learn [1] Chapter: "14.3.2 Alignment of Constructed Types" and @read_again [1] Chapter: "14.3.2 Alignment of Constructed Types" Lua types vs. NDR constructed types This isn't quite as trivial as with primitive types but the correspondence should exist and be implemented for the sake of easy MSRPC/NDR programming. The demands are very easy to comprehend but their realisation is somewhat difficult. NDR arrays, for example, should have Lua arrays as correspondents and NDR structures should have Lua tables as correspondents. Once again the motivation for all this is to make MSRPC/NDR programming easy and more alike its C language birth place. Type construct Whishing to generalize every NDR type I made a somewhat general construct. This provided us with more intuitive way when thinking about packing complicated constructed types. So every type can be dissasembled into parts: * Conformant arrays - a type contains conformant and/or conformant and varying arrays. * Pointers - a type containts reference, full and unique pointers. * Body - every other type contained. Motivation for this formalisation is that when packing a constructed type the max count information located in conf and conf_var arrays, also called conformant information, often precedes the constructed type (structure packing) and pointers are always deferred to a later position. The body part is the one that does not precede or deferr. Constructed type context Any NDR contructed type can be found in few contexes. If a type is a procedure parameter that type is in TOP LEVEL context. If a type is a field inside the structure then that type is in STRUCTURE context. If a type is an array element then this type is in ARRAY context. And if a type is a union element then this type is in UNION context. When a type is only in one context, not mixed, then we will call that the EXCLUSIVE context, otherwise its the MIXED context. The motivation for this formalisation is that constructed types are packed differently according to the context they are in. Note: According to the Open Group specification of NDR even alignment is context specific, so for example if an array is in structure context its alignment is defined as the largest alignment of the array element type and the size information type, if any. But testing shows that Microsoft doesn't comply with such specification so NDR_Type:getAlignment() methods receive the context flags but make no use of it. That is every alignment is calculated without considering the actual context. ]]-- local top_lvl_ctx = 0x00 local struct_ctx = 0x01 local arr_ctx = 0x02 local uni_ctx = 0x04 local function inCtx(ctxs, which_ctx) return bit.band(ctxs, which_ctx) == which_ctx end local function inExCtx(ctxs, which_ctx) return ctxs == which_ctx end local function mixCtx(ctxs, which_ctx) return bit.bor(ctxs, which_ctx) end NDR_ConstructedType = NDR_Type:new { new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self o:setName("unnamed constructed type") return o end, setVal = function(self, lua_val, prototype) self.lua_val = lua_val self:setPrototype(prototype) end, prototype = {}, setPrototype = function(self, prototype) self.prototype = prototype end, getPrototype = function(self) return self.prototype end, representation = {}, setRep = function(self, rep) self.representation = rep end, getRep = function(self) return self.representation end, push = function(self, ndr, ctx_flags) end, pull = function(self, ndr, ctx_flags) end } --####################################################################-- --# 2.1) Arrays --# @see [1] Chapter: "14.3.3 Arrays" --####################################################################-- --[[ An array is an ordered, indexed collection of elements of a single type. The elements of an array can be of any NDR primitive or constructed type except arrays, pipes, conformant structures and context handles. NDR arrays: * Unidimensional or multidimensional * Conformant (unknown max count before hand) * Varying (the actual count varies among calls) Special cases: 1. Array of strings 2. Structures that contain some kinds of arrays 3. Arrays that contain pointers 3) If a pointer is embedded in an array, structure or union, the representation of its REFERENT IS DEFERRED to a position in the octet stream that follows the representation of the construction that embeds the pointer. Representations of pointer referents are ORDERED according to a LEFT-TO-RIGHT, DEPTH-FIRST traversal of the embedding construction. For the complete elaboration on deferral algorithm: * @see [1] Chapter: "14.3.12.3 Algorithm for Deferral of Referents" * @learn [1] Chapter: "14.3.12.3 Algorithm for Deferral of Referents" * @read_again [1] Chapter: "14.3.12.3 Algorithm for Deferral of Referents" SPECIAL CASE: The special case of an array of reference pointers embedded in a structure has no NDR representation, that is; there is no 4-byte unspecified value transmitted. --Microsoft Windows NDR engine doesn't comply with this specification so we can ignore it. ]]-- local function deepcopy(object) local lookup_table = {} local function _copy(object) if type(object) ~= "table" then return object elseif lookup_table[object] then return lookup_table[object] end local new_table = {} lookup_table[object] = new_table for index, value in pairs(object) do new_table[_copy(index)] = _copy(value) end return setmetatable(new_table, getmetatable(object)) end return _copy(object) end local function CalcArrayDims(arr) local dims = {} local t = arr while type(t) == "table" and t[1] ~= nil do table.insert(dims, table.getn(t)) t = t[1] end return dims end local function FlattenArray(arr, flat) if(type(arr) ~= "table" or arr[1] == nil) then table.insert(flat, arr) return end for i=1,table.getn(arr),1 do FlattenArray(arr[i], flat) end end local function lua2ndrArray(lua_arr, pt_arr_elem) if(type(lua_arr) ~= "table" or lua_arr[1] == nil) then local lua_arr_elem = lua_arr local ndr_arr_elem = pt_arr_elem:make_ndr(lua_arr_elem) return ndr_arr_elem end local ndr_arr = {} for i=1,#lua_arr,1 do table.insert( ndr_arr, lua2ndrArray(lua_arr[i], pt_arr_elem) ) end return ndr_arr end local function ndr2luaArray(ndr_arr, lua_arr) if(type(ndr_arr) ~= "table" or ndr_arr[1] == nil) then local ndr_arr_elem = ndr_arr return ndr_arr_elem:getVal() end for i=1,#ndr_arr,1 do lua_arr[i] = lua_arr[i] or {} lua_arr[i+1] = nil lua_arr[i] = ndr2luaArray(ndr_arr[i], lua_arr[i]) end return lua_arr end local function makeNDRArr(lua_arr, dims, pt_arr_elem) if(#dims == 1) then local ndr_arr = {} for i=1,dims[1],1 do if(lua_arr == nil) then table.insert(ndr_arr, pt_arr_elem:make_ndr(nil)) else table.insert(ndr_arr, pt_arr_elem:make_ndr(lua_arr[i])) end end return ndr_arr else local ndr_arr = {} local next_dims = deepcopy(dims) --copy!!! table.remove(next_dims, 1) for i=1,dims[1],1 do if(lua_arr == nil) then table.insert(ndr_arr, makeNDRArr(nil, next_dims, pt_arr_elem)) else table.insert(ndr_arr, makeNDRArr(lua_arr[i], next_dims, pt_arr_elem)) end end return ndr_arr end end NDR_FixedArr = NDR_ConstructedType:new { dims = {}, fixed_arr = {}, pt_fixed_arr = {}, new = function(self, lua_arr, pt_fixed_arr, o) o = o or {} setmetatable(o, self) self.__index = self o:setName("fixed array") o:setVal(lua_arr, pt_fixed_arr) return o end, setVal = function(self, lua_arr, pt_fixed_arr) getmetatable(self):setVal(lua_arr, pt_fixed_arr) self.pt_fixed_arr = pt_fixed_arr local pt_arr_elem self.dims, pt_arr_elem = unpack(pt_fixed_arr:getMap()) if(lua_arr == nil) then self.fixed_arr = makeNDRArr(nil, self.dims, pt_arr_elem) else --self.fixed_arr = lua2ndrArray(lua_arr, pt_arr_elem) self.fixed_arr = makeNDRArr(lua_arr, self.dims, pt_arr_elem) end getmetatable(self):setRep(self.fixed_arr) end, getVal = function(self, lua_arr) lua_arr = lua_arr or {} ndr2luaArray(self.fixed_arr, lua_arr) return lua_arr end, getFlat = function(self) local flat = {} FlattenArray(self.fixed_arr, flat) return flat end, push = function(self, ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fixed_arr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() push_size, ndr_error = self:push_elements(ndr, ctx_flags) if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:push] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PUSH"], self:getName() ) ndr_error:Activate(error_msg) return push_size, ndr_error end --debug NDR_Debug:deindent() return push_size, ndr_error end, push_elements = function(self, ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fixed_arr][push_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local fixed_arr_flat = self:getFlat() for _,element in pairs(fixed_arr_flat) do local ps, ne = element:push(ndr, mixCtx(ctx_flags,arr_ctx)) push_size = push_size + ps ndr_error = ne if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:push_elements] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PUSH_ELEM"], self:getName() ) ndr_error:Activate(error_msg) return push_size, ndr_error end end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do local ps, ne = pointer:push_referent(ndr) push_size = push_size + ps ndr_error = ne if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:push_elements] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PUSH_ELEM"], self:getName() ) ndr_error:Activate(error_msg) return push_size, ndr_error end end end --debug NDR_Debug:deindent() return push_size, ndr_error end, pull = function(self, ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fixed_arr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() pull_size, ndr_error = self:pull_elements(ndr, ctx_flags) if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:pull] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PULL"], self:getName() ) ndr_error:Activate(error_msg) return pull_size, ndr_error end --debug NDR_Debug:deindent() return pull_size, ndr_error end, pull_elements = function(self, ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fixed_arr][pull_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local fixed_arr_flat = self:getFlat() for _,element in pairs(fixed_arr_flat) do local ps, ne = element:pull(ndr, mixCtx(ctx_flags,arr_ctx)) pull_size = pull_size + ps ndr_error = ne if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:pull_elements] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PULL_ELEM"], self:getName() ) ndr_error:Activate(error_msg) return pull_size, ndr_error end end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do local ps, ne = pointer:pull_referent(ndr) push_size = push_size + ps ndr_error = ne if(ndr_error:isActive()) then local error_msg = string.format( "[NDR_FixedArr:pull_elements] %s (%s)", NDR_Error.ERRORS["NDR_ARR_PULL_ELEM"], self:getName() ) ndr_error:Activate(error_msg) return push_size, ndr_error end end end --debug NDR_Debug:deindent() return pull_size, ndr_error end, getAlignment = function(self, ctx_flags) ctx_flags = ctx_flags or top_lvl_ctx return self:getFlat()[1]:getAlignment() end, getParts = function(self, conf_arrays, alignments, body, pointers) table.insert(alignments, self:getAlignment()) --fixed array are non-deferrals so add yourself to the body list. table.insert(body, self) local fixed_arr_flat = self:getFlat() for _,element in pairs(fixed_arr_flat) do element:getParts( conf_arrays, {}, {}, pointers--embedded pointers follow a constructed type so... ) end end } NDR_ConfArr = NDR_Type:new() function NDR_ConfArr:new(lua_arr, pt_conf_arr) local o = {} setmetatable(o, self) self.__index = self o.conf_arr = {} o.dims = {} o.max_counts = {} o.pt_conf_arr = {} o:setVal(lua_arr, pt_conf_arr) return o end function NDR_ConfArr:setVal(lua_arr, pt_conf_arr) self.pt_conf_arr = pt_conf_arr local pt_arr_elem self.max_counts, pt_arr_elem = unpack(pt_conf_arr:getMap()) if(lua_arr == nil) then self.conf_arr = makeNDRArr(nil, self.max_counts, pt_arr_elem) else self.max_counts = CalcArrayDims(lua_arr) self.conf_arr = makeNDRArr(lua_arr, self.max_counts, pt_arr_elem) end for i=1,#self.max_counts,1 do table.insert(self.dims, self.max_counts[i]) end end function NDR_ConfArr:getVal(lua_arr) lua_arr = lua_arr or {} ndr2luaArray(self.conf_arr, lua_arr) return lua_arr end function NDR_ConfArr:getFlat() local flat = {} FlattenArray(self.conf_arr, flat) return flat end function NDR_ConfArr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_arr][push] .." NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then self:push_max_counts(ndr) end self:push_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfArr:getMaxCounts() return self.max_counts end function NDR_ConfArr:push_max_counts(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_arr][push_max_counts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, table.getn(self.max_counts),1 do ndr:push_unsigned_long(NDR_UnsignedLong:new(self.max_counts[i])) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfArr:push_elements(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --[[ if(struct_ctx) then local conf_arr_al = self:getAlignment() local conf_arr_pad = get_pad(ndr.push_blob, conf_arr_al) ndr:push(conf_arr_pad) end ]]-- --debug local debug_msg debug_msg = "[conf_arr][push_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_arr_flat = self:getFlat() for _,element in pairs(conf_arr_flat) do element:push(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:push_referent(ndr) end end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfArr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_arr][pull] .." NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then self:pull_max_counts(ndr) end self:pull_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfArr:getMaxCounts() return self.max_counts end function NDR_ConfArr:pull_max_counts(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_arr][pull_max_counts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, #self.dims,1 do local ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.max_counts[i] = ndr_long:getVal() end local _,pt_arr_elem = unpack(self.pt_conf_arr:getMap()) self.conf_arr = makeNDRArr(nil, self.max_counts, pt_arr_elem) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfArr:pull_elements(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_arr][pull_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_arr_flat = self:getFlat() for _,element in pairs(conf_arr_flat) do element:pull(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:pull_referent(ndr) end end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfArr:getAlignment(ctx_flags) ctx_flags = ctx_flags or top_lvl_ctx local max_size_alignment = NDR_UnsignedLong:getAlignment() local elements_alignment = self:getFlat()[1]:getAlignment() --[[ if(inCtx(ctx_flags, struct_ctx)) then if(elements_alignment < max_size_alignment) then return max_size_alignment else return elements_alignment end else return elements_alignment end ]]-- return elements_alignment end function NDR_ConfArr:getParts(conf_arrays, alignments, body, pointers) table.insert(conf_arrays, self) table.insert(alignments, self:getAlignment()) local conf_arr_flat = self:getFlat() for _, element in pairs(conf_arr_flat) do element:getParts( conf_arrays, {}, {}, pointers) end end ---Marshall a NDR n-dimensional conformant array type and push it onto a ndr stack. -- @ndr_desc NDR represents a conformant array as an ordered sequence of representations of the array --elements, preceded by an unsigned long integer. The integer gives the number of array elements --transmitted, including empty elements. A conformant array can contain at most 2^32−1 elements. -- @see [1] Chapter: "14.3.3.2 Uni-dimensional Conformant Arrays" function NDR:push_conf_arr(ndr_conf_arr) return self, ndr_conf_arr:push(self) end function NDR:pull_conf_arr(ndr_conf_arr) return self, ndr_conf_arr:pull(self) end NDR_VarArr = NDR_Type:new() function NDR_VarArr:new(lua_arr, pt_var_arr) local o = {} setmetatable(o, self) self.__index = self o.dims = {} o.actual_counts = {} o.offsets = {} o.pt_var_arr = {} o:setVal(lua_arr, pt_var_arr) return o end function NDR_VarArr:setVal(lua_arr, pt_var_arr) self.pt_var_arr = pt_var_arr local pt_arr_elem self.actual_counts, pt_arr_elem = unpack(pt_var_arr:getMap()) if(lua_arr == nil) then self.var_arr = makeNDRArr(nil, self.actual_counts, pt_arr_elem) else self.actual_counts = CalcArrayDims(lua_arr) self.var_arr = makeNDRArr(lua_arr, self.actual_counts, pt_arr_elem) end for i=1,#self.actual_counts,1 do table.insert(self.offsets, 0) table.insert(self.dims, self.actual_counts[i]) end end function NDR_VarArr:getVal(lua_arr) lua_arr = lua_arr or {} ndr2luaArray(self.var_arr, lua_arr) return lua_arr end function NDR_VarArr:getFlat() local flat = {} FlattenArray(self.var_arr, flat) return flat end function NDR_VarArr:getOffsets() return self.offsets end function NDR_VarArr:getActualCounts() return self.actual_counts end function NDR_VarArr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[var_arr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() self:push_offs_and_act_cnts(ndr) self:push_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_VarArr:push_offs_and_act_cnts(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[var_arr][push_offs_cnts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1,table.getn(self.dims),1 do ndr:push_unsigned_long(NDR_UnsignedLong:new(self.offsets[i])) ndr:push_unsigned_long(NDR_UnsignedLong:new(self.actual_counts[i])) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_VarArr:push_elements(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[var_arr][push_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local var_arr_flat = self:getFlat() for _,element in pairs(var_arr_flat) do element:push(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:push_referent(ndr) end end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_VarArr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[var_arr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() self:pull_offs_and_act_cnts(ndr) self:pull_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_VarArr:pull_offs_and_act_cnts(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[var_arr][pull_offs_cnts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, #self.dims,1 do local ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.offsets[i] = ndr_long:getVal() ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.actual_counts[i] = ndr_long:getVal() self.dims[i] = ndr_long:getVal() end local _,pt_arr_elem = unpack(self.pt_var_arr:getMap()) self.var_arr = makeNDRArr(nil, self.actual_counts, pt_arr_elem) --now we have actual counts and offsets, make yourself --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_VarArr:pull_elements(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[var_arr][pull_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local var_arr_flat = self:getFlat() for _,element in pairs(var_arr_flat) do element:pull(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:pull_referent(ndr) end end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_VarArr:getAlignment(ctx_flags) local elements_alignment = self:getFlat()[1]:getAlignment() return elements_alignment end function NDR_VarArr:getParts(conf_arrays, alignments, body, pointers) table.insert(alignments, self:getAlignment()) table.insert(body, self) local var_arr_flat = self:getFlat() for _, element in pairs(var_arr_flat) do element:getParts( conf_arrays, {}, {}, pointers) end end ---Marshall a NDR n-dimensional varying array type and push it onto a ndr stack. -- @ndr_desc NDR represents a varying array as an ordered sequence of representations of the array elements, --preceded by two unsigned long integers. The first integer gives the offset from the first index of --the array to the first index of the actual subset being passed. The second integer gives the actual --number of elements being passed. A varying array can contain at most 232−1 elements. -- @see [1] Chapter: "14.3.3.3 Uni-dimensional Varying Arrays" function NDR:push_var_arr(ndr_var_arr) return self, ndr_var_arr:push(self) end function NDR:pull_var_arr(ndr_var_arr) return self, ndr_var_arr:pull(self) end NDR_ConfVarArr = NDR_Type:new() function NDR_ConfVarArr:new(lua_arr, pt_conf_var_arr) local o = {} setmetatable(o, self) self.__index = self o.dims = {} o.max_counts = {} o.actual_counts = {} o.offsets = {} o.pt_conf_var_arr = {} o:setVal(lua_arr, pt_conf_var_arr) return o end function NDR_ConfVarArr:setVal(lua_arr, pt_conf_var_arr) self.pt_conf_var_arr = pt_conf_var_arr local pt_arr_elem self.actual_counts, pt_arr_elem = unpack(pt_conf_var_arr:getMap()) if(lua_arr == nil) then self.conf_var_arr = makeNDRArr(nil, self.actual_counts, pt_arr_elem) else self.actual_counts = CalcArrayDims(lua_arr) self.conf_var_arr = makeNDRArr(lua_arr, self.actual_counts, pt_arr_elem) end for i=1,#self.actual_counts,1 do table.insert(self.offsets, 0) table.insert(self.dims, self.actual_counts[i]) table.insert(self.max_counts, self.actual_counts[i]) end end function NDR_ConfVarArr:getVal(lua_arr) lua_arr = lua_arr or {} ndr2luaArray(self.conf_var_arr, lua_arr) return lua_arr end function NDR_ConfVarArr:getFlat() local flat = {} FlattenArray(self.conf_var_arr, flat) return flat end function NDR_ConfVarArr:getOffsets() return self.offsets end function NDR_ConfVarArr:getActualCounts() return self.actual_counts end function NDR_ConfVarArr:getMaxCounts() return self.max_counts end function NDR_ConfVarArr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_var_arr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then self:push_max_counts(ndr) end self:push_offs_and_act_cnts(ndr) self:push_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfVarArr:push_max_counts(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_var_arr][push_max_counts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, table.getn(self.max_counts),1 do ndr:push_unsigned_long(NDR_UnsignedLong:new(self.max_counts[i])) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfVarArr:push_offs_and_act_cnts(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_var_arr][push_off_cnts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1,table.getn(self.dims),1 do ndr:push_unsigned_long(NDR_UnsignedLong:new(self.offsets[i])) ndr:push_unsigned_long(NDR_UnsignedLong:new(self.actual_counts[i])) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfVarArr:push_elements(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_var_arr][push_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_var_arr_flat = self:getFlat() for _,element in pairs(conf_var_arr_flat) do element:push(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:push_referent(ndr) end end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_ConfVarArr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_var_arr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then self:pull_max_counts(ndr) end self:pull_offs_and_act_cnts(ndr) self:pull_elements(ndr, ctx_flags) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfVarArr:pull_max_counts(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_var_arr][pull_max_counts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, #self.dims,1 do local ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.max_counts[i] = ndr_long:getVal() end local _,pt_arr_elem = unpack(self.pt_conf_var_arr:getMap()) self.conf_var_arr = makeNDRArr(nil, self.max_counts, pt_arr_elem) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfVarArr:pull_offs_and_act_cnts(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[conf_var_arr][pull_offs_cnts] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() for i=1, #self.dims,1 do local ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.offsets[i] = ndr_long:getVal() ndr_long = NDR_UnsignedLong:new(nil) ndr:pull_unsigned_long(ndr_long) self.actual_counts[i] = ndr_long:getVal() self.dims[i] = ndr_long:getVal() end local _,pt_arr_elem = unpack(self.pt_conf_var_arr:getMap()) self.conf_var_arr = makeNDRArr(nil, self.actual_counts, pt_arr_elem) --now we have actual counts and offsets, make yourself --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfVarArr:pull_elements(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[conf_var_arr][pull_elements] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_var_arr_flat = self:getFlat() for _,element in pairs(conf_var_arr_flat) do element:pull(ndr, mixCtx(ctx_flags, arr_ctx)) end if(inExCtx(ctx_flags, top_lvl_ctx)) then local pointers = {} self:getParts({}, {}, {}, pointers) for _,pointer in pairs(pointers) do pointer:pull_referent(ndr) end end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_ConfVarArr:getAlignment(ctx_flags) ctx_flags = ctx_flags or top_lvl_ctx local max_size_alignment = NDR_UnsignedLong:getAlignment() local elements_alignment = self:getFlat()[1]:getAlignment() --[[ if(inCtx(ctx_flags, struct_ctx)) then if(elements_alignment < max_size_alignment) then return max_size_alignment else return elements_alignment end else return elements_alignment end ]]-- return elements_alignment end function NDR_ConfVarArr:getParts(conf_arrays, alignments, body, pointers) table.insert(conf_arrays, self) table.insert(alignments, self:getAlignment()) local conf_var_arr_flat = self:getFlat() for _, element in pairs(conf_var_arr_flat) do element:getParts( conf_arrays, {}, {}, pointers) end end ---Marshall a NDR n-dimensional conformant and varying array type and push it onto a ndr stack. -- @ndr_desc NDR represents a conformant and varying array as an ordered sequence of representations of --the array elements, preceded by three unsigned long integers. The first integer gives the --maximum number of elements in the array. The second integer gives the offset from the first --index of the array to the first index of the actual subset being passed. The third integer gives the --actual number of elements being passed. A conformant and varying array can contain at most 232−1−o elements, --where o is the offset. The integers that indicate the offset and the actual count are always present, --even if the maximum count is 0 (zero). -- @see [1] Chapter: "14.3.3.4 Uni-dimensional Conformant-varying Arrays" function NDR:push_conf_var_arr(ndr_conf_var_arr) return self, ndr_conf_var_arr:push(self) end function NDR:pull_conf_var_arr(ndr_conf_var_arr) return self, ndr_conf_var_arr:pull(self) end --####################################################################-- --# 2) NDR Structures --# @see [1] Chapter: "14.3.6 Structures" --####################################################################-- --[[ A structure is an ordered collection of members, not necessarily all of the same type. A structure member can be of any NDR primitive or constructed type. Rules: * A CONFORMANT ARRAYS can appear in a structure only as the last member. * A STRUCTURE THAT CONTAINS A CONFORMANT ARRAY can appear in another structure only as the last member. NDR represents a structure as an ordered sequence of representations of the structure members. NDR defines special representations for: 1. Structures that contain a conformant array or a conformant and varying array. 2. Structures that contain pointers. 1) A structure can contain a conformant or a conformant and varying array only as its last member. In the NDR representation of a structure that contains a conformant or a conformant and varying array, the unsigned long integers that give maximum element counts for dimensions of the array are moved to the beginning of the structure, but the offsets and actual counts of the conformant and varying array remain in place at the end of the structure, immediately preceding the array elements. If a structure that contains a conformant or a conformant and varying array itself a member of another structure, the maximum element counts are further moved to the beginning of the containing structure. This construction iterates through all enclosing structures. @see [1] Chapter: "14.3.7.1 Structures Containing a Conformant Array" @see [1] Chapter: "14.3.7.2 Structures Containing a Conformant and Varying Array" 2) ]]-- NDR_Struct = NDR_Type:new() function NDR_Struct:new(lua_struct, pt_struct) local o = {} setmetatable(o, self) self.__index = self o.struct = {} o.pt_struct = {} o:setVal(lua_struct, pt_struct) return o end function NDR_Struct:setVal(lua_struct, pt_struct) self.pt_struct = pt_struct local struct_map = pt_struct:getMap() if(lua_struct ~= nil) then for i=1,#struct_map,1 do table.insert(self.struct, { field=struct_map[i][1], ndr_type=struct_map[i][2]:make_ndr( lua_struct[struct_map[i][1]] ) } ) end else for i=1,#struct_map,1 do table.insert(self.struct, { field=struct_map[i][1], ndr_type=struct_map[i][2]:make_ndr(nil) } ) end end end function NDR_Struct:getVal() local lua_struct = {} for i=1,#self.struct,1 do lua_struct[self.struct[i].field] = self.struct[i].ndr_type:getVal() end return lua_struct end function NDR_Struct:getAlignment(ctx_flags) ctx_flags = ctx_flags or top_lvl_ctx local max_alignment = 1 for i=1,#self.struct,1 do local alignment alignment = self.struct[i].ndr_type:getAlignment() if(max_alignment < alignment) then max_alignment = alignment end end return max_alignment end function NDR_Struct:getParts(conf_arrays, alignments, body, pointers) for i=1,#self.struct,1 do self.struct[i].ndr_type:getParts( conf_arrays, alignments, body, pointers) end end function NDR_Struct:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() --[[ Packing algorithm: 1) If in exclusive top level context: * Get parts using recursive depth first search. * Pack conformant information. * Pack the structure alignment and body. * Pack the pointer referents. 2) If in structure context: * Get parts using recursive depth first search. * Pack the structure alignment and body. * Conformant information and pointer referents are packed by the outer structure. 3) If in exclusive array context: * Get parts using recursive depth first search. * Pack conformant information. * Pack the structure alignment and body. * Pointer referents are packed by the outer array. ]]-- ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[struct][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_arrays = {} local alignments = {} local body = {} local pointers = {} self:getParts(conf_arrays, alignments, body, pointers) --debug debug_msg = "[struct][conf_info] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then --if top level structure then pack the conformant array max counts --else it's going to be packed by the outer structure for _,conf_array in pairs(conf_arrays) do conf_array:push_max_counts(ndr) end end --debug NDR_Debug:deindent() --a structure ALWAYS packs its alignment local max_alignment = 1 for _,alignment in pairs(alignments) do if(max_alignment < alignment) then max_alignment = alignment end end local struct_pad = get_pad(ndr.push_blob, max_alignment) ndr:push(struct_pad) --debug debug_msg = string.format("[struct][align] pad=%dB", #struct_pad) NDR_Debug:debug(debug_msg) --debug debug_msg = "[struct][body] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() --a structure ALWAYS packs its non deferred body parts for _,b in pairs(body) do b:push(ndr, mixCtx(ctx_flags, struct_ctx)) end --debug NDR_Debug:deindent() --debug debug_msg = "[struct][conf_deferr] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then --if not int structure then pack the conformant array elements --else it's going to be packed by the outer structure for _,conf_array in pairs(conf_arrays) do conf_array:push(ndr, mixCtx(ctx_flags, struct_ctx)) end end --debug NDR_Debug:deindent() --debug debug_msg = "[struct][pointer_deferr] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(inExCtx(ctx_flags, top_lvl_ctx)) then --if top level structure then pack the pointer referents --else it's going to be packed by the outer structure, array for _,pointer in pairs(pointers) do pointer:push_referent(ndr, mixCtx(ctx_flags, struct_ctx)) end end --debug NDR_Debug:deindent() --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_Struct:pull(ndr, ctx_flags) local ndr_error = NDR_Error:new() local pull_size = 0 ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[struct][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() local conf_arrays = {} local alignments = {} local body = {} local pointers = {} self:getParts(conf_arrays, alignments, body, pointers) --debug debug_msg = "[struct][conf_info] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then for _,conf_array in pairs(conf_arrays) do conf_array:pull_max_counts(ndr) end end --debug NDR_Debug:deindent() --a structure ALWAYS packs its alignment local max_alignment = 1 for _,alignment in pairs(alignments) do if(max_alignment < alignment) then max_alignment = alignment end end local struct_pad = get_pad( string.sub(ndr.pull_blob, 1, ndr.pull_blob_pos - 1), max_alignment) ndr:pull(#struct_pad) --debug debug_msg = string.format("[struct][align] pad=%dB", #struct_pad) NDR_Debug:debug(debug_msg) --debug debug_msg = "[struct][body] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() --a structure ALWAYS packs its non deferred body parts for _,b in pairs(body) do pull_size = b:pull(ndr, mixCtx(ctx_flags, struct_ctx)) end --debug NDR_Debug:deindent() --debug debug_msg = "[struct][conf_deferr] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inCtx(ctx_flags, struct_ctx)) then for _,conf_array in pairs(conf_arrays) do conf_array:pull(ndr, mixCtx(ctx_flags, struct_ctx)) end end --debug NDR_Debug:deindent() --debug debug_msg = "[struct][pointer_deferr] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(inExCtx(ctx_flags, top_lvl_ctx)) then for _,pointer in pairs(pointers) do pointer:pull_referent(ndr, mixCtx(ctx_flags, struct_ctx)) end end --debug NDR_Debug:deindent() --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR:push_struct(ndr_struct) return self, ndr_struct:push(self) end function NDR:pull_struct(ndr_struct) return self, ndr_struct:pull(self) end --####################################################################-- --# 2) NDR Pointers --# @see [1] Chapter: "14.3.10 Pointers" --####################################################################-- --[[ NDR defines two classes of pointers that differ both in semantics and in representation: * reference pointers, which cannot be null and cannot be aliases * full pointers, which can be null and can be an aliases * unique pointers, which can be null and cannot be aliases, AND ARE TRANSMiTED AS FULL POINTERS. Furthermore, a pointer can be: * Top level -- parameters in remote procedure calls. * Embedded -- are elements of arrays, members of structures or members of unions. a) NULL pointer If a pointer points to nothing, it is NULL. b)PRIMARY pointer and ALIASES If the input and output octet streams pertaining to one remote procedure call contain several pointers that point to the same thing, the first of these pointers to be transmitted is considered primary and the others are considered aliases. The scope of aliasing for a pointer extends to all streams transmitted in the service of one remote procedure call; that is, any inputs in the request that initiates the call, and any outputs in the response to the call. Aliasing does not apply to null pointers. c) TOP-LEVEL and EMBEDDED pointers We refer to pointers that are parameters in remote procedure calls as top-level pointers and we refer to pointers that are elements of arrays, members of structures, or members of unions as embedded pointers. NDR defines different representations for top-level and embedded pointers. ]]-- NDR_RefPtr = NDR_Type:new() function NDR_RefPtr:new(lua_ptr, pt_ref_ptr) local o = {} setmetatable(o, self) self.__index = self o.referent = {} o.referent_id = {} o.pt_ref_ptr = {} o:setVal(lua_ptr, pt_ref_ptr) return o end function NDR_RefPtr:setVal(lua_ptr, pt_ref_ptr) self.pt_ref_ptr = pt_ref_ptr local pt_ptr_referent = pt_ref_ptr:getMap() self.referent_id = NDR_UnsignedLong:new(math.random(0xabcd)) if(lua_ptr == nil) then self.referent = pt_ptr_referent:make_ndr(nil) else self.referent = pt_ptr_referent:make_ndr(lua_ptr.ref) end end function NDR_RefPtr:getVal(lua_ptr) if(self.referent_id:getVal() == 0) then lua_ptr = nil else lua_ptr = lua_ptr or {} lua_ptr.ref = self.referent:getVal() end return lua_ptr end function NDR_RefPtr:push_referent_id(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[refptr][push_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:push_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_RefPtr:push_referent(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[refptr][push_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() self.referent:push(ndr, ctx_flags) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_RefPtr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() --[[ Packing algorithm 1) If in exclusive top level context: * Pack only the referent. 2) If not in exlusive top level context: * Pack only the referent id. * The referent itself is going to be packed by the outer structure, array or union. ]]-- ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[refptr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:push_referent_id(ndr) else self:push_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_RefPtr:pull_referent_id(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[refptr][pull_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:pull_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_RefPtr:pull_referent(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[refptr][pull_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() self.referent:pull(ndr, ctx_flags) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_RefPtr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[refptr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:pull_referent_id(ndr) else self:pull_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_RefPtr:getAlignment() return 4 end function NDR_RefPtr:getParts(conf_arrays, alignments, body, pointers) --you're a pointer, insert yourself table.insert(pointers, self) --you have a body, if top_lvl_ctx then your referent else it's your ref_id table.insert(body, self) --you have an alignment, insert it table.insert(alignments, self:getAlignment()) self.referent:getParts( conf_arrays, alignments, {}, pointers) end function NDR:push_ref_ptr(ndr_ref_ptr) return self, ndr_ref_ptr:push(self) end function NDR:pull_ref_ptr(ndr_ref_ptr) return self, ndr_ref_ptr:pull(self) end NDR_FullPtr = NDR_Type:new() function NDR_FullPtr:new(lua_ptr, pt_full_ptr) local o = {} setmetatable(o, self) self.__index = self o.referent = {} o.referent_id = {} o:setVal(lua_ptr, pt_full_ptr) return o end function NDR_FullPtr:setVal(lua_ptr, pt_full_ptr) local full_ptr_map = pt_full_ptr:getMap() self.referent = full_ptr_map:make_ndr(lua_ptr.ref) self.referent_id = NDR_UnsignedLong:new(Pointers:getReferentId(lua_ptr, ptr_full_flag)) end function NDR_FullPtr:getVal() return self.referent end function NDR_FullPtr:push_referent_id(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[fullptr][push_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:push_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_FullPtr:push_referent(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fullptr][push_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not ndr:isAlias(self)) then self.referent:push(ndr, ctx_flags) ndr:setAlias(self) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_FullPtr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() --[[ Packing algorithm 1) If an alias: * Pack the referent id only 2) If in exclusive top level context: * Pack the referent id * Pack the referent (if not an alias). 3) If not in exlusive top level context: * Pack only the referent id. * The referent itself is going to be packed by the outer structure, array or union (if not an alias). ]]-- ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fullptr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:push_referent_id(ndr) else self:push_referent_id(ndr) self:push_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_FullPtr:pull_referent_id(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[fullptr][pull_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:pull_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_FullPtr:pull_referent(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fullptr][pull_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(self.referent_id:getVal() ~= 0) then self.referent:pull(ndr, ctx_flags) end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_FullPtr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[fullptr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:pull_referent_id(ndr) else self:pull_referent_id(ndr) self:pull_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_FullPtr:getAlignment() return 4 end function NDR_FullPtr:getParts(conf_arrays, alignments, body, pointers) --you're a pointer, insert yourself table.insert(pointers, self) --you have a body, referent id! table.insert(body, self) --you have an alignment, insert it table.insert(alignments, self:getAlignment()) self.referent:getParts( conf_arrays, alignments, {}, pointers) end function NDR_FullPtr:getReferent() return self.referent end function NDR_FullPtr:getReferentId() return self.referent_id end function NDR:push_full_ptr(ndr_full_ptr) return self, ndr_full_ptr:push(self) end function NDR:pull_full_ptr(ndr_full_ptr) return self, ndr_full_ptr:pull(self) end NDR_UniPtr = NDR_Type:new() function NDR_UniPtr:new(lua_ptr, pt_uni_ptr) local o = {} setmetatable(o, self) self.__index = self o.referent = {} o.referent_id = {} o.pt_uni_ptr = {} o:setVal(lua_ptr, pt_uni_ptr) return o end function NDR_UniPtr:setVal(lua_ptr, pt_uni_ptr) self.pt_uni_ptr = pt_uni_ptr local pt_ptr_referent = pt_uni_ptr:getMap() self.referent_id = NDR_UnsignedLong:new( Pointers:getReferentId(lua_ptr, ptr_unique_flag)) if(lua_ptr == nil) then self.referent = pt_ptr_referent:make_ndr(nil) else self.referent = pt_ptr_referent:make_ndr(lua_ptr.ref) end end function NDR_UniPtr:getVal(lua_ptr) if(self.referent_id:getVal() == 0) then lua_ptr = nil else lua_ptr = lua_ptr or {} lua_ptr.ref = self.referent:getVal() end return lua_ptr end function NDR_UniPtr:push_referent_id(ndr) local push_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[uniptr][push_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:push_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_UniPtr:push_referent(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[uniptr][push_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(self.referent_id:getVal() ~= 0) then self.referent:push(ndr, ctx_flags) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_UniPtr:push(ndr, ctx_flags) local push_size = 0 local ndr_error = NDR_Error:new() --[[ Packing algorithm 1) If an alias: * Pack the referent id only 2) If in exclusive top level context: * Pack the referent id * Pack the referent (if not an alias). 3) If not in exlusive top level context: * Pack only the referent id. * The referent itself is going to be packed by the outer structure, array or union (if not an alias). ]]-- ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[uniptr][push] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:push_referent_id(ndr) else self:push_referent_id(ndr) self:push_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return push_size, ndr_error end function NDR_UniPtr:pull_referent_id(ndr) local pull_size = 0 local ndr_error = NDR_Error:new() --debug local debug_msg debug_msg = "[uniptr][pull_ref_id] @long" NDR_Debug:debug(debug_msg) NDR_Debug:indent() ndr:pull_unsigned_long(self.referent_id) --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_UniPtr:pull_referent(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[uniptr][pull_referent] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(self.referent_id:getVal() ~= 0) then self.referent:pull(ndr, ctx_flags) end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_UniPtr:pull(ndr, ctx_flags) local pull_size = 0 local ndr_error = NDR_Error:new() ctx_flags = ctx_flags or top_lvl_ctx --debug local debug_msg debug_msg = "[uniptr][pull] .." NDR_Debug:debug(debug_msg) NDR_Debug:indent() if(not inExCtx(ctx_flags, top_lvl_ctx)) then self:pull_referent_id(ndr) else self:pull_referent_id(ndr) self:pull_referent(ndr, ctx_flags) end --debug NDR_Debug:deindent() return pull_size, ndr_error end function NDR_UniPtr:getAlignment() return 4 end function NDR_UniPtr:getParts(conf_arrays, alignments, body, pointers) --you're a pointer, insert yourself table.insert(pointers, self) --you have a body, referent id! table.insert(body, self) --you have an alignment, insert it table.insert(alignments, self:getAlignment()) self.referent:getParts( conf_arrays, alignments, {}, pointers) end function NDR_UniPtr:getReferent() return self.referent end function NDR_UniPtr:getReferentId() return self.referent_id end function NDR:push_uni_ptr(ndr_uni_ptr) return self, ndr_uni_ptr:push(self) end function NDR:pull_uni_ptr(ndr_uni_ptr) return self, ndr_uni_ptr:pull(self) end --####################################################################-- --# Lua type complements --####################################################################-- --[[ Lua type complements are lua constructs used to emulate missing lua types, that the NDR provides. NDRs birth place is C language, so it supports all of the C types and beyond. NDR primitive types all have a corresponding native Lua type, therefore they don't have a lua complement: * small, short, long, hyper, char, octet and enum -- are Lua numbers (of specific range ofcourse) * bool -- are Lua booleans (true|false) Most of NDR constructed types don't have a native Lua type so we introduce their Lua representations explicitely. The goal here is to make them more C like and easy to handle. So: * Arrays -- well they are represented as Lua arrays, for example: local arr = {1,2,3,4,5,6} * Structures -- they are represented as Lua tables, for example: local struct = {} struct.sm = 1 struct.arr = {1,2,3,4} struct.str = {sh = 2, l = 4} * Pointers -- here we introduce a non-lua native construct. The goal here was to emulate references, and the first thing that came to mind was to use the tables. Tables have a unique identifier, a reference, so they are perfect for the job. local ptra = {ref = 3} ]]-- --####################################################################-- --# Lua to NDR mappings --####################################################################-- --[[ Prototypes and maps Prototypes are used to convert/transform/map a Lua type to a correspondent NDR type. A prototype is a generic image of some NDR type, primitive or constructed. By generic I mean that it does not represent the exact mapping of the Lua type to the correspondent NDR type, it merely holds the information and methods used to make such a mapping. A prototype can be primitive or constructed, as NDR types can :): * Primitive -- simple, used for mapping from Lua to NDR primitive type. * Constructed -- holds a map which is constructed of other prototypes, primitive or constructed. A map is a part of a prototype, it's constructed of other prototypes. A map holds the information of how to construct the prototype and make the exact mapping from Lua to NDR. NDR_Prototype class represents a prototype. It's a simple class, it holds a map and a mapping method. NDR_Prototype.new = function (map) NDR_Prototype.make_ndr = function (lua_type) NDR_Prototype.map = {} So a prototype is used as a factory, given a correct lua type it will generate an exact mapping into NDR, using make_ndr() method. Every NDR type has a corresponing prototype, but note that primitive type don't have a map as they already have a corresponing Lua type, thus a mapping doesn't have to occur. NDR constructed types don't have a corresponing native lua type, and as such mapping must occur. Therefore me must define a "map" syntax for each constructed type prototype. Array map syntax "An array is an ordered, indexed collection of elements of a single type". This definition leads us to make a very simple array "map". An array "map" is actually its element prototype. So let's demonstate with an example: local lua_arr = {1,2,3,4,5} --let's say we want to convert it to a NDR fixed array local pt_small = PT_Small:new() --see how a NDR primitive prototype constructor don't have a map local pt_fixed_arr = PT_FixedArr(pt_small) --array map is actually its element prototype local ndr_fixed_arr = NDR_FixedArr:new(lua_arr, pt_fixed_arr) --see how a NDR_Type constructor takes a corresponding lua type and --a prototype, and then return a corresponing NDR_Type Structure map syntax "A structure is an ORDERED collection of members, not necessarily all of the same type.". This definition led us to make a following structure map syntax: struct_map = { {"field_name1", field_pt1}, {"field_name2", field_pt2}, {"field_name3", field_pt3}, {"field_nameN", field_ptN} } This construct describes the previous definition very well. Strings represent the fields name and the second part represents the field prototype. Now the "map" definition seems more intuitive: "map consists of prototypes". So let's demonstrate with an example: local lua_struct_1 = {} lua_struct_1.arr = {5,6,7,8} local lua_struct_2 = {} lua_struct_2.sm = 1 lua_struct_2.sh = 2 lua_struct_2.arr = {1,2,3,4,5} lua_struct_2.s = lua_struct_1 --let's assume that all the arrays are fixed arrays and numbers are long local pt_long = PT_Long:new() local pt_struct_1_arr = PT_FixedArr:new(pt_long) local pt_struct_1 = PT_Struct:new( { {"arr", pt_struct_1_arr} } ) --see how the struct map look like. local pt_small = PT_Small:new() local pt_short = PT_Small:new() local pt_struct_2_arr = PT_FixedArr:new(pt_long) local pt_struct_2 = PT_Struct:new( { {"sm", pt_small} {"sh", pt_short} {"arr", pt_struct_2_arr} {"s", pt_struct_1} } local ndr_struct_2 = NDR_Struct:new(lua_struct_2, pt_struct_2) --and finally me make our NDR_Struct using the constructed structure --prototype and a corresponding lua type. ]]-- NDR_Prototype = {} function NDR_Prototype:new(map) local o = {} setmetatable(o, self) self.__index = self o.map = map return o end function NDR_Prototype:make_ndr(lua_type) end function NDR_Prototype:getMap() return self.map end PT_UnsignedSmall = NDR_Prototype:new() function PT_UnsignedSmall:new(unsigned_small_map) local o = {} setmetatable(o, self) self.__index = self o.map = unsigned_small_map --nil actually as every primitive has an corresponind Lua type return o end function PT_UnsignedSmall:make_ndr(lua_unsigned_small) return NDR_UnsignedSmall:new(lua_unsigned_small) end PT_SignedSmall = NDR_Prototype:new() function PT_SignedSmall:new(signed_small_map) local o = {} setmetatable(o, self) self.__index = self o.map = signed_small_map --nil actually as every primitive has an corresponind Lua type return o end function PT_SignedSmall:make_ndr(lua_signed_small) return NDR_SignedSmall:new(lua_signed_small) end PT_UnsignedShort = NDR_Prototype:new() function PT_UnsignedShort:new(unsigned_short_map) local o = {} setmetatable(o, self) self.__index = self o.map = unsigned_short_map --nil actually as every primitive has an corresponind Lua type return o end function PT_UnsignedShort:make_ndr(lua_unsigned_short) return NDR_UnsignedShort:new(lua_unsigned_short) end PT_SignedShort = NDR_Prototype:new() function PT_SignedShort:new(signed_short_map) local o = {} setmetatable(o, self) self.__index = self o.map = signed_short_map --nil actually as every primitive has an corresponind Lua type return o end function PT_SignedShort:make_ndr(lua_signed_short) return NDR_SignedShort:new(lua_signed_short) end PT_UnsignedLong = NDR_Prototype:new() function PT_UnsignedLong:new(unsigned_long_map) local o = {} setmetatable(o, self) self.__index = self o.map = unsigned_long_map --nil actually as every primitive has an corresponind Lua type return o end function PT_UnsignedLong:make_ndr(lua_unsigned_long) return NDR_UnsignedLong:new(lua_unsigned_long) end PT_SignedLong = NDR_Prototype:new() function PT_SignedLong:new(signed_long_map) local o = {} setmetatable(o, self) self.__index = self o.map = signed_long_map --nil actually as every primitive has an corresponind Lua type return o end function PT_SignedLong:make_ndr(lua_signed_long) return NDR_SignedLong:new(lua_signed_long) end PT_UnsignedHyper = NDR_Prototype:new() function PT_UnsignedHyper:new(unsigned_hyper_map) local o = {} setmetatable(o, self) self.__index = self o.map = unsigned_hyper_map --nil actually as every primitive has an corresponind Lua type return o end function PT_UnsignedHyper:make_ndr(lua_unsigned_hyper) return NDR_UnsignedHyper:new(lua_unsigned_hyper) end PT_SignedHyper = NDR_Prototype:new() function PT_SignedHyper:new(signed_hyper_map) local o = {} setmetatable(o, self) self.__index = self o.map = signed_hyper_map --nil actually as every primitive has an corresponind Lua type return o end function PT_SignedHyper:make_ndr(lua_signed_hyper) return NDR_SignedHyper:new(lua_signed_hyper) end PT_Char = NDR_Prototype:new() function PT_Char:new(char_map) local o = {} setmetatable(o, self) self.__index = self o.map = char_map --nil actually as every primitive has an corresponind Lua type return o end function PT_Char:make_ndr(lua_char) return NDR_Char:new(lua_char) end PT_Octet = NDR_Prototype:new() function PT_Octet:new(octet_map) local o = {} setmetatable(o, self) self.__index = self o.map = octet_map --nil actually as every primitive has an corresponind Lua type return o end function PT_Octet:make_ndr(lua_octet) return NDR_Octet:new(lua_octet) end PT_Enum = NDR_Prototype:new() function PT_Enum:new(enum_map) local o = {} setmetatable(o, self) self.__index = self o.map = enum_map --nil actually as every primitive has an corresponind Lua type return o end function PT_Enum:make_ndr(lua_enum) return NDR_Enum:new(lua_enum) end PT_Bool = NDR_Prototype:new() function PT_Bool:new(bool_map) local o = {} setmetatable(o, self) self.__index = self o.map = bool_map --nil actually as every primitive has an corresponind Lua type return o end function PT_Bool:make_ndr(lua_bool) return NDR_Bool:new(lua_bool) end PT_FixedArr = NDR_Prototype:new() function PT_FixedArr:new(arr_map) local o = {} setmetatable(o, self) self.__index = self o.map = arr_map return o end function PT_FixedArr:make_ndr(lua_arr) return NDR_FixedArr:new(lua_arr, self) end PT_ConfArr = NDR_Prototype:new() function PT_ConfArr:new(arr_map) local o = {} setmetatable(o, self) self.__index = self o.map = arr_map return o end function PT_ConfArr:make_ndr(lua_arr) return NDR_ConfArr:new(lua_arr, self) end PT_VarArr = NDR_Prototype:new() function PT_VarArr:new(arr_map) local o = {} setmetatable(o, self) self.__index = self o.map = arr_map return o end function PT_VarArr:make_ndr(lua_arr) return NDR_VarArr:new(lua_arr, self) end PT_ConfVarArr = NDR_Prototype:new() function PT_ConfVarArr:new(arr_map) local o = {} setmetatable(o, self) self.__index = self o.map = arr_map return o end function PT_ConfVarArr:make_ndr(lua_arr) return NDR_ConfVarArr:new(lua_arr, self) end PT_Struct = NDR_Prototype:new() function PT_Struct:new(struct_map) local o = {} setmetatable(o, self) self.__index = self o.map = struct_map return o end function PT_Struct:make_ndr(lua_struct) return NDR_Struct:new(lua_struct, self) end PT_RefPtr = NDR_Prototype:new() function PT_RefPtr:new(ref_ptr_map) local o = {} setmetatable(o, self) self.__index = self o.map = ref_ptr_map return o end function PT_RefPtr:make_ndr(lua_ref_ptr) return NDR_RefPtr:new(lua_ref_ptr, self) end PT_FullPtr = NDR_Prototype:new() function PT_FullPtr:new(full_ptr_map) local o = {} setmetatable(o, self) self.__index = self o.map = full_ptr_map return o end function PT_FullPtr:make_ndr(lua_full_ptr) return NDR_FullPtr:new(lua_full_ptr, self) end PT_UniPtr = NDR_Prototype:new() function PT_UniPtr:new(uni_ptr_map) local o = {} setmetatable(o, self) self.__index = self o.map = uni_ptr_map return o end function PT_UniPtr:make_ndr(lua_uni_ptr) return NDR_UniPtr:new(lua_uni_ptr, self) end --there's no need to make new instances of primitive prototypes pt_unsigned_small = PT_UnsignedSmall:new() pt_unsigned_short = PT_UnsignedShort:new() pt_unsigned_long = PT_UnsignedLong:new() pt_unsigned_hyper = PT_UnsignedHyper:new() pt_signed_small = PT_SignedSmall:new() pt_signed_short = PT_SignedShort:new() pt_signed_long = PT_SignedLong:new() pt_signed_hyper = PT_SignedHyper:new() pt_char = PT_Char:new() pt_octet = PT_Octet:new() pt_bool = PT_Bool:new() pt_enum = PT_Enum:new() --####################################################################-- --# IDL to NDR mappings --# @see [1] Chapter: "Interface Definition Language" --####################################################################-- --[[ IDL - NDR small - small short - short long - long hyper - hyper boolean - boolean byte - uninterpreted octet enum - enum type type_array[KONST] - unidimensional fixed array [max_is(size_expr)] arr[] - conformant array [size_is(size_expr)] arr[] - conformant array [length_is(size_expr)] arr[KONST] - varying array [first_is(size_expr), length_is(size_expr)] arr[KONST] - varying array [last_is(size_expr)] arr[KONST] - varying array [size_is(size_expr), length_is(size_expr)] arr[] - conformant varying array --]] --####################################################################-- --# UTILITY --###################################################################-- --####################################################################-- ---Makes a pad for alignment -- @param data Data which needs to be padded for the sake of alignment. -- @param align Integer representing the alignment boundary. -- @param pad_byte The value for pad byte. -- @return Returns the amount of pad calculated by (align-datalen%align)%align. --####################################################################-- function get_pad(data, align, pad_byte) pad_byte = pad_byte or "\00" return string.rep(pad_byte, (align-string.len(data)%align)%align) end --####################################################################-- ---Generates a random string of the requested length. --@param length The length of the string to return. --@param set The set of letters to choose from. Default: ASCII letters and numbers --@return The random string. --####################################################################-- function random_crap(length, charset) charset = charset or "0123456789abcdefghijklmnoprstuvzxwyABCDEFGHIJKLMNOPRSTUVZXWY" local random_str = "" for i = 1, length, 1 do local random = math.random(#charset) random_str = random_str .. string.sub(charset, random, random) end return random_str end