local datetime = require "datetime"
local ipOps = require "ipOps"
local math = require "math"
local msrpctypes = require "msrpctypes"
local netbios = require "netbios"
local os = require "os"
local smb = require "smb"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local unicode = require "unicode"
_ENV = stdnse.module("msrpc", stdnse.seeall)
SAMR_PATH       = "\\samr"
SAMR_UUID       = "\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xac"
SAMR_VERSION    = 0x01
SRVSVC_PATH     = "\\srvsvc"
SRVSVC_UUID     = "\xc8\x4f\x32\x4b\x70\x16\xd3\x01\x12\x78\x5a\x47\xbf\x6e\xe1\x88"
SRVSVC_VERSION  = 0x03
SPOOLSS_PATH    = "\\spoolss"
SPOOLSS_UUID = "\x78\x56\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xab"
SPOOLSS_VERSION = 0x01
LSA_PATH        = "\\lsarpc"
LSA_UUID        = "\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xab"
LSA_VERSION     = 0
WINREG_PATH     = "\\winreg"
WINREG_UUID     = "\x01\xd0\x8c\x33\x44\x22\xf1\x31\xaa\xaa\x90\x00\x38\x00\x10\x03"
WINREG_VERSION  = 1
SVCCTL_PATH    = "\\svcctl"
SVCCTL_UUID    = "\x81\xbb\x7a\x36\x44\x98\xf1\x35\xad\x32\x98\xf0\x38\x00\x10\x03"
SVCCTL_VERSION = 2
ATSVC_PATH     = "\\atsvc"
ATSVC_UUID     = "\x82\x06\xf7\x1f\x51\x0a\xe8\x30\x07\x6d\x74\x0b\xe8\xce\xe9\x8b"
ATSVC_VERSION  = 1
EPMAPPER_PATH = "\\epmapper"
EPMAPPER_UUID     = "\x08\x83\xaf\xe1\x1f\x5d\xc9\x11\x91\xa4\x08\x00\x2b\x14\xa0\xfa"
EPMAPPER_VERSION  = 3
TRANSFER_SYNTAX = "\x04\x5d\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00\x2b\x10\x48\x60"
REFERENT_ID = 0x50414d4e
MAX_FRAGMENT = 0x800
local SAMR_GROUPSIZE = 20
local LSA_GROUPSIZE  = 20
local LSA_MINEMPTY = 10
local UUID2EXE = {
  ["1ff70682-0a51-30e8-076d-740be8cee98b"] = "mstask.exe atsvc interface (Scheduler service)",
  ["3faf4738-3a21-4307-b46c-fdda9bb8c0d5"] = "AudioSrv AudioSrv interface (Windows Audio service)",
  ["6bffd098-a112-3610-9833-012892020162"] = "Browser browser interface (Computer Browser service)",
  ["91ae6020-9e3c-11cf-8d7c-00aa00c091be"] = "certsrv.exe ICertPassage interface (Certificate services)",
  ["5ca4a760-ebb1-11cf-8611-00a0245420ed"] = "termsrv.exe winstation_rpc interface",
  ["c8cb7687-e6d3-11d2-a958-00c04f682e16"] = "WebClient davclntrpc interface (WebDAV client service)",
  ["50abc2a4-574d-40b3-9d66-ee4fd5fba076"] = "dns.exe DnsServer interface (DNS Server service)",
  ["e1af8308-5d1f-11c9-91a4-08002b14a0fa"] = "RpcSs epmp interface (RPC endpoint mapper)",
  ["82273fdc-e32a-18c3-3f78-827929dc23ea"] = "Eventlog eventlog interface (Eventlog service)",
  ["3d267954-eeb7-11d1-b94e-00c04fa3080d"] = "lserver.exe Terminal Server Licensing",
  ["894de0c0-0d55-11d3-a322-00c04fa321a1"] = "winlogon.exe InitShutdown interface",
  ["8d0ffe72-d252-11d0-bf8f-00c04fd9126b"] = "CryptSvc IKeySvc interface (Cryptographic services)",
  ["0d72a7d4-6148-11d1-b4aa-00c04fb66ea0"] = "CryptSvc ICertProtect interface (Cryptographic services)",
  ["d6d70ef0-0e3b-11cb-acc3-08002b1d29c4"] = "locator.exe NsiS interface (RPC Locator service)",
  ["342cfd40-3c6c-11ce-a893-08002b2e9c6d"] = "llssrv.exe llsrpc interface (Licensing Logging service)",
  ["12345778-1234-abcd-ef00-0123456789ab"] = "lsass.exe lsarpc interface",
  ["3919286a-b10c-11d0-9ba8-00c04fd92ef5"] = "lsass.exe dssetup interface",
  ["5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc"] = "messenger msgsvcsend interface (Messenger service)",
  ["2f5f3220-c126-1076-b549-074d078619da"] = "netdde.exe nddeapi interface (NetDDE service)",
  ["4fc742e0-4a10-11cf-8273-00aa004ae673"] = "Dfssvc netdfs interface (Distributed File System service)",
  ["12345678-1234-abcd-ef00-01234567cffb"] = "Netlogon netlogon interface (Net Logon service)",
  ["8d9f4e40-a03d-11ce-8f69-08003e30051b"] = "PlugPlay pnp interface (Plug and Play service)",
  ["d335b8f6-cb31-11d0-b0f9-006097ba4e54"] = "PolicyAgent PolicyAgent interface (IPSEC Policy Agent (Windows 2000))",
  ["369ce4f0-0fdc-11d3-bde8-00c04f8eee78"] = "winlogon.exe pmapapi interface",
  ["c9378ff1-16f7-11d0-a0b2-00aa0061426a"] = "lsass.exe IPStoreProv interface (Protected Storage)",
  ["8f09f000-b7ed-11ce-bbd2-00001a181cad"] = "mprdim.dll Remote Access",
  ["12345778-1234-abcd-ef00-0123456789ac"] = "lsass.exe samr interface",
  ["93149ca2-973b-11d1-8c39-00c04fb984f9"] = "services.exe SceSvc",
  ["12b81e99-f207-4a4c-85d3-77b42f76fd14"] = "seclogon ISeclogon interface (Secondary logon service)",
  ["83da7c00-e84f-11d2-9807-00c04f8ec850"] = "winlogon.exe sfcapi interface (Windows File Protection)",
  ["4b324fc8-1670-01d3-1278-5a47bf6ee188"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) srvsvc interface (Server service)",
  ["4b112204-0e19-11d3-b42b-0000f81feb9f"] = "ssdpsrv ssdpsrv interface (SSDP service)",
  ["367aeb81-9844-35f1-ad32-98f038001003"] = "services.exe svcctl interface (Services control manager)",
  ["2f5f6520-ca46-1067-b319-00dd010662da"] = "Tapisrv tapsrv interface (Telephony service)",
  ["300f3532-38cc-11d0-a3f0-0020af6b0add"] = "Trkwks trkwks interface (Distributed Link Tracking Client)",
  ["8fb6d884-2388-11d0-8c35-00c04fda2795"] = "w32time w32time interface (Windows Time)",
  ["a002b3a0-c9b7-11d1-ae88-0080c75e4ec1"] = "winlogon.exe GetUserToken interface",
  ["338cd001-2244-31f1-aaaa-900038001003"] = "RemoteRegistry winreg interface (Remote registry service)",
  ["45f52c28-7f9f-101a-b52b-08002b2efabe"] = "wins.exe winsif interface (WINS service)",
  ["6bffd098-a112-3610-9833-46c3f87e345a"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) wkssvc interface (Workstation service)"
}
function start_smb(host, path, disable_extended, overrides)
  overrides = overrides or {}
  local _, sharename = smb.get_fqpn(host, "IPC$")
  return smb.start_ex(host, true, true, sharename, path, disable_extended, overrides)
end
function stop_smb(state)
  smb.stop(state)
end
function bind(smbstate, interface_uuid, interface_version, transfer_syntax)
  local status, result
  local parameters, data
  local pos, align
  local result
  stdnse.debug2("MSRPC: Sending Bind() request")
  if(transfer_syntax == nil) then
    transfer_syntax = TRANSFER_SYNTAX
  end
  data = string.pack("<BBBB>I4<I2I2I4I2I2I4BxxxI2Bx",
    0x05, -- Version (major)
    0x00, -- Version (minor)
    0x0B, -- Packet type (0x0B = bind)
    0x03, -- Packet flags (0x03 = first frag + last frag)
    0x10000000, -- Data representation (big endian)
    0x0048,     -- Frag length
    0x0000,     -- Auth length
    0x41414141, -- Call ID (I use 'AAAA' because it's easy to recognize)
    MAX_FRAGMENT, -- Max transmit frag
    MAX_FRAGMENT, -- Max receive frag
    0x00000000, -- Assoc group
    0x01,       -- Number of items
    0x0000,            -- Context ID
    0x01)              -- Number of transaction items. */
  .. interface_uuid    -- Interface (eg. SRVSVC UUID: 4b324fc8-1670-01d3-1278-5a47bf6ee188)
  .. string.pack("<I2I2",
    interface_version, -- Interface version (major)
    0x0000)            -- Interface version (minor)
  .. transfer_syntax   -- Transfer syntax
  .. string.pack("<I4", 2) -- Syntax version
  status, result = smb.write_file(smbstate, data, 0)
  if(status ~= true) then
    return false, result
  end
  status, result = smb.read_file(smbstate, 0, MAX_FRAGMENT)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: Received Bind() result")
  parameters = result['parameters']
  data = result['data']
  local fmt = "<BBBB>I4<I2I2I4"
  if #data < string.packsize(fmt) then
    return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
  end
  result.version_major, result.version_minor, result.packet_type, result.packet_flags,
    result.data_representation, result.frag_length, result.auth_length,
    result.call_id, pos = string.unpack(fmt, data)
  if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
    return false, "Bind() returned a fault (packet type)"
  end
  if((result['packet_flags'] & 0x20) == 0x20) then
    return false, "Bind() returned a fault (flags)"
  end
  if(result['auth_length'] ~= 0) then
    return false, "Bind() returned an 'auth length', which we don't know how to deal with"
  end
  if((result['packet_flags'] & 0x03) ~= 0x03) then
    return false, "Bind() returned a fragmented packet, which we don't know how to handle"
  end
  if(result['packet_type'] ~= 0x0c) then
    return false, "Bind() returned an unexpected packet type (not BIND_ACK)"
  end
  if(result['call_id'] ~= 0x41414141) then
    return false, "MSRPC call returned an incorrect 'call_id' value"
  end
  local fmt = "<I2I2I4I2"
  if #data - pos + 1 < string.packsize(fmt) then
    return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
  end
  result.max_transmit_frag, result.max_receive_frag, result.assoc_group,
    result.secondary_address_length, pos = string.unpack(fmt, data, pos)
  if #data - pos + 1 < result.secondary_address_length + 1 then -- +1 to account for num_results below
    return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
  end
  result.secondary_address, pos = string.unpack(("<c%d"):format(result.secondary_address_length), data, pos)
  pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment -- don't ask how I came up with this, it was a lot of drawing, and there's probably a far better way
  result.num_results, pos = string.unpack("<B", data, pos)
  pos = pos + ((4 - ((pos - 1) % 4)) % 4); -- Alignment
  if(result['num_results'] ~= 1) then
    return false, "Bind() returned the incorrect number of result"
  end
  local fmt = "<I2I2c16I4"
  if #data - pos + 1 < string.packsize(fmt) then
    return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
  end
  result.ack_result, result.align, result.transfer_syntax, result.syntax_version, pos = string.unpack(fmt, data, pos)
  return true, result
end
function call_function(smbstate, opnum, arguments)
  local status, result
  local parameters, data
  local pos, align
  local result
  local first = true
  local is_first, is_last
  data = string.pack("<BBBB>I4<I2I2I4I4I2I2",
    0x05,        -- Version (major)
    0x00,        -- Version (minor)
    0x00,        -- Packet type (0x00 = request)
    0x03,        -- Packet flags (0x03 = first frag + last frag)
    0x10000000,  -- Data representation (big endian)
    0x18 + #arguments, -- Frag length (0x18 = the size of this data)
    0x0000,      -- Auth length
    0x41414141,  -- Call ID (I use 'AAAA' because it's easy to recognize)
    #arguments,  -- Alloc hint
    0x0000,      -- Context ID
    opnum)       -- Opnum
  .. arguments
  stdnse.debug3("MSRPC: Calling function 0x%02x with %d bytes of arguments", opnum, #arguments)
  status, result = smb.write_file(smbstate, data, 0)
  if(status ~= true) then
    return false, result
  end
  local arguments = ""
  repeat
    status, result = smb.read_file(smbstate, 0, 0x1001)
    if(status ~= true) then
      return false, result
    end
    parameters = result['parameters']
    data       = result['data']
    local fmt = "<BBBB>I4<I2I2I4"
    if #data < string.packsize(fmt) then
      return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
    end
    result.version_major, result.version_minor, result.packet_type,
    result.packet_flags, result.data_representation, result.frag_length,
    result.auth_length, result.call_id, pos = string.unpack(fmt, data)
    is_first = ((result['packet_flags'] & 0x01) == 0x01)
    is_last  = ((result['packet_flags'] & 0x02) == 0x02)
    if(first == true and is_first == false) then
      return false, "MSRPC: First fragment doesn't have proper 'first' (0x01) flag set"
    end
    if(first == false and is_first) then
      return false, "MSRPC: Middle (or last) fragment doesn't have proper 'first' (0x01) flag set"
    end
    if(result['packet_type'] == 0x03) then -- MSRPC_FAULT
      return false, "MSRPC call returned a fault (packet type)"
    end
    if((result['packet_flags'] & 0x20) == 0x20) then
      return false, "MSRPC call returned a fault (flags)"
    end
    if(result['auth_length'] ~= 0) then
      return false, "MSRPC call returned an 'auth length', which we don't know how to deal with"
    end
    if(result['packet_type'] ~= 0x02) then
      return false, "MSRPC call returned an unexpected packet type (not RESPONSE)"
    end
    if(result['call_id'] ~= 0x41414141) then
      return false, "MSRPC call returned an incorrect 'call_id' value"
    end
    local fmt = "<I4I2BB"
    if #data - pos + 1 < string.packsize(fmt) then
      return false, "MSRPC: ERROR: Ran off the end of SMB packet; likely due to server truncation"
    end
    result.alloc_hint, result.context_id, result.cancel_count, align, pos = string.unpack(fmt, data, pos)
    arguments = arguments .. string.sub(data, pos)
    first = false
  until is_last == true
  result['arguments'] = arguments
  stdnse.debug3("MSRPC: Function call successful, %d bytes of returned arguments", #result['arguments'])
  return true, result
end
function call_lanmanapi(smbstate, opnum, paramdesc, datadesc, data)
  local status, result
  local parameters = ""
  local pos
  parameters = string.pack("<I2zz",
    opnum,
    paramdesc,  -- Parameter Descriptor
    datadesc)      -- Return Descriptor
  .. data
  stdnse.debug1("MSRPC: Sending Browser Service request")
  status, result = smb.send_transaction_named_pipe(smbstate, parameters, nil, "\\PIPE\\LANMAN", true)
  if(not(status)) then
    return false, "Couldn't call LANMAN API: " .. result
  end
  return true, result
end
function rap_netserverenum2(smbstate, domain, server_type, detail_level)
  local NETSERVERENUM2 = 0x0068
  local paramdesc = (domain and "WrLehDz" or "WrLehDO")
  assert( detail_level > 0 and detail_level < 2, "detail_level must be either 0 or 1")
  local datadesc = ( detail_level == 0 and "B16" or "B16BBDz")
  local data = string.pack("<I2I2I4", detail_level,
  14724,
  server_type)
  .. (domain or "")
  local status, result = call_lanmanapi(smbstate, NETSERVERENUM2, paramdesc, datadesc, data )
  if ( not(status) ) then
    return false, "MSRPC: NetServerEnum2 call failed"
  end
  local parameters = result.parameters
  local data = result.data
  stdnse.debug1("MSRPC: Parsing Browser Service response")
  local status, convert, entry_count, available_entries, pos = string.unpack("<I2I2I2I2", parameters)
  if(status ~= 0) then
    return false, string.format("Call to Browser Service failed with status = %d", status)
  end
  stdnse.debug1("MSRPC: Browser service returned %d entries", entry_count)
  local pos = 1
  local entries = {}
  for i = 1, entry_count, 1 do
    local server = {}
    server.name, pos = string.unpack("<z", data, pos)
    stdnse.debug1("MSRPC: Found name: %s", server.name)
    pos = pos + ( 16 - (#server.name % 16) ) - 1
    if ( detail_level > 0 ) then
      local comment_offset, _
      server.version = {}
      server.version.major, server.version.minor,
        server.type, comment_offset, _, pos = string.unpack("<BBI4I2I2", data, pos)
      server.comment, pos = string.unpack("<z", data, (comment_offset - convert + 1))
    end
    table.insert(entries, server)
  end
  return true, entries
end
function srvsvc_ShareType_tostr(val)
  return msrpctypes.srvsvc_ShareType_tostr(val)
end
function srvsvc_netshareenumall(smbstate, server)
  local status, result
  local arguments
  local pos, align
  local level
  local ctr, referent, count, max_count
  stdnse.debug2("MSRPC: Calling NetShareEnumAll() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_srvsvc_NetShareCtr(0, {array=nil})
  .. msrpctypes.marshall_int32(4096)
  .. msrpctypes.marshall_int32_ptr(0)
  status, result = call_function(smbstate, 0x0F, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetShareEnumAll() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetShareCtr(arguments, pos, level)
  if(pos == nil) then
    return false, "unmarshall_srvsvc_NetShareCtr() returned an error"
  end
  pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netshareenumall)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netshareenumall)"
  end
  return true, result
end
function srvsvc_netsharegetinfo(smbstate, server, share, level)
  stdnse.debug2("Calling NetShareGetInfo(%s, %s, %d)", server, share, level)
  local _, _, sharename = string.find(share, "\\\\.*\\(.*)")
  if sharename then
    share = sharename
  end
  local arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true)
  .. msrpctypes.marshall_unicode(share, true)
  .. msrpctypes.marshall_int32(level)
  local status, result = call_function(smbstate, 0x10, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetShareGetInfo() returned successfully")
  arguments = result['arguments']
  local pos = 1
  pos, result['info'] = msrpctypes.unmarshall_srvsvc_NetShareInfo(arguments, pos)
  if(pos == nil) then
    return false, "unmarshall_srvsvc_NetShareInfo() returned an error"
  end
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netsharegetinfo)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netsharegetinfo)"
  end
  return true, result
end
function srvsvc_netsessenum(smbstate, server)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling NetSessEnum() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_unicode_ptr(nil)
  .. msrpctypes.marshall_unicode_ptr(nil)
  .. msrpctypes.marshall_int32(10) -- 10 seems to be the only useful one allowed anonymously
  .. msrpctypes.marshall_srvsvc_NetSessCtr(10, {array=nil})
  .. msrpctypes.marshall_int32(0xFFFFFFFF)
  .. msrpctypes.marshall_int32_ptr(0)
  status, result = call_function(smbstate, 0x0C, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetSessEnum() returned successfully")
  arguments = result['arguments']
  pos = 1
  local count
  local sessions = {}
  local referent_id
  pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetSessCtr(arguments, pos)
  if(pos == nil) then
    return false, "unmarshall_srvsvc_NetSessCtr() returned an error"
  end
  pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netsessenum)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netsessenum)"
  end
  return true, result
end
function srvsvc_netservergetstatistics(smbstate, server)
  local status, result
  local arguments
  local pos, align
  local service = "SERVICE_SERVER"
  stdnse.debug2("MSRPC: Calling NetServerGetStatistics() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_unicode_ptr(service, true)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0)
  status, result = call_function(smbstate, 0x18, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetServerGetStatistics() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['stat'] = msrpctypes.unmarshall_srvsvc_Statistics_ptr(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netservergetstatistics)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netservergetstatistics)"
  end
  return true, result
end
function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathflags)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling NetPathCompare(%s, %s) [%s]", path1, path2, smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_unicode(path1, true)
  .. msrpctypes.marshall_unicode(path2, true)
  .. msrpctypes.marshall_int32(pathtype)
  .. msrpctypes.marshall_int32(pathflags)
  status, result = call_function(smbstate, 0x20, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetPathCompare() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netpathcompare)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcompare)"
  end
  return true, result
end
function srvsvc_netpathcanonicalize(smbstate, server, path)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling NetPathCanonicalize(%s) [%s]", path, smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_unicode(path, true)
  .. msrpctypes.marshall_int32(2)
  .. msrpctypes.marshall_unicode("\\", true)
  .. msrpctypes.marshall_int32(1)
  .. msrpctypes.marshall_int32(1)
  status, result = call_function(smbstate, 0x1F, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: NetPathCanonicalize() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['max_count'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['can_path']  = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['type']      = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return']    = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (srvsvc.netpathcanonicalize)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcanonicalize)", result
  end
  return true, result
end
function spoolss_open_printer(smbstate,printer)
  local machine = msrpctypes.marshall_unicode_ptr("",true)
  local user = msrpctypes.marshall_unicode_ptr("",true)
  local arguments = msrpctypes.marshall_unicode_ptr(printer,true)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0x02020000)
  .. msrpctypes.marshall_int32(1)
  .. msrpctypes.marshall_int32(1)
  .. msrpctypes.marshall_int32(12345135)
  local arguments2 =  string.sub(machine,1,4)
  ..  string.sub(user,1,4)
  .. msrpctypes.marshall_int32(7600)
  .. msrpctypes.marshall_int32(3)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(9)
  .. string.sub(machine,5,#machine)
  .. string.sub(user,5,#user)
  arguments2 = msrpctypes.marshall_int32(#arguments2+4) .. arguments2
  local status, result = call_function(smbstate, 69, arguments .. arguments2)
  if not status then
    stdnse.debug1("MSRPC spoolss_open_printer(): %s ",result)
  end
  return status,result
end
function spoolss_start_doc_printer(smbstate,printer_handle,filename)
  local document_name = msrpctypes.marshall_unicode_ptr("nmap_test",true)
  local fname = msrpctypes.marshall_unicode_ptr(filename,true)
  local dtype = msrpctypes.marshall_int32(0)
  local arguments = printer_handle .. msrpctypes.marshall_int32(1)
  local document_container = msrpctypes.marshall_int32(1)
  .. msrpctypes.marshall_int32(12332131)
  .. string.sub(document_name,1,4)
  .. string.sub(fname,1,4)
  .. string.sub(dtype,1,4)
  .. string.sub(document_name,5,#document_name)
  .. string.sub(fname,5,#fname)
  .. string.sub(dtype,5,#dtype)
  local status, result = call_function(smbstate, 17, arguments .. document_container)
  if not status then
    stdnse.debug1("MSRPC spoolss_start_doc_printer(): %s",result)
  end
  return status,result
end
function spoolss_write_printer(smbstate,printer_handle,data)
  local padding_len = 4 - math.fmod(#data,4)
  local data_padding = nil
  if not (padding_len == 4) then
    data_padding = string.rep('\0', padding_len)
  end
  local arguments = printer_handle ..  msrpctypes.marshall_int32(#data)
  .. data
  .. (data_padding or "")
  .. msrpctypes.marshall_int32(#data)
  local status,result = call_function(smbstate, 19, arguments)
  if not status then
    stdnse.debug1("MSRPC spoolss_write_printer(): %s",result)
  end
  return status,result
end
function spoolss_end_doc_printer(smbstate,printer_handle)
  local status,result = call_function(smbstate,23,printer_handle)
  if not status then
    stdnse.debug1("MSRPC spoolss_end_doc_printer(): %s",result)
  end
  return status,result
end
function spoolss_abort_printer(smbstate,printer_handle)
  local status,result = call_function(smbstate,21,printer_handle)
  if not status then
    stdnse.debug1("MSRPC spoolss_abort_printer(): %s",result)
  end
  return status,result
end
function uuid_to_string(uuid)
  local A, B, C, D, E = string.unpack("<I4I2I2>c2c6", uuid)
  return ("%08x-%04x-%04x-%s-%s"):format(A, B, C, stdnse.tohex(D), stdnse.tohex(E))
end
function string_uuid_to_exe(uuid)
  return UUID2EXE[uuid]
end
function epmapper_lookup(smbstate,handle)
  if handle == nil then -- if it's a first request, send a null handle
    handle = string.rep('\0', 20)
  end
  local params = msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0) .. msrpctypes.marshall_int32(0)
  .. handle .. msrpctypes.marshall_int32(1)
  local status,result = call_function(smbstate,2,params)
  if not status then
    stdnse.debug1("MSRPC epmapper_lookup(): %s",result)
  end
  local data = result.data
  local pos
  local lookup_response = {
    new_handle = nil,
    annotation  = nil,
    uuid  = nil,
    exe = nil,
    tcp_port = nil,
    udp_port = nil,
    ip_addr = nil,
    ncalrpc = nil,
    ncacn_np = nil,
    netbios = nil,
    ncacn_http = nil
  }
  lookup_response.new_handle = string.sub(data,25,44)
  local num_entries
  num_entries, pos = string.unpack("<I4", data, 45)
  if num_entries == 0 then
    return false, "finished"
  end
  pos = pos + 12
  pos = pos + 16
  pos = pos + 8
  local annotation_length
  annotation_length, pos = string.unpack("<I4", data, pos)
  if annotation_length > 1 then
    lookup_response.annotation = string.sub(data,pos,pos+annotation_length-2)
  end
  local padding = (4-(annotation_length%4))
  if padding == 4 then padding = 0 end
  pos = pos + annotation_length + padding
  pos = pos + 8
  local num_floors,floor_len,uuid, address_type,address_len,tcp_port,udp_port,ip_addr,saved_pos,ncalrpc,ncacn_np,netbios,ncacn_http
  num_floors, pos = string.unpack("<I2", data, pos)
  for i = 1, num_floors do
    saved_pos = pos
    floor_len, pos = string.unpack("<I2", data, pos)
    if i == 1 then
      uuid = string.sub(data,pos+1,pos+16)
      lookup_response.uuid = uuid_to_string(uuid)
      lookup_response.exe = string_uuid_to_exe(lookup_response.uuid)
    else
      if not (i == 2) and not (i == 3) then        -- just skip floor 2 and 3
        address_type, address_len, pos = string.unpack("<BI2", data, pos)
        if address_type == 0x07 then
          lookup_response.tcp_port, pos = string.unpack(">I2", data, pos)
        elseif address_type == 0x08 then
          lookup_response.udp_port, pos = string.unpack(">I2", data, pos)
        elseif address_type == 0x09 then
          local ip
          ip, pos = string.unpack("c4", data, pos)
          lookup_response.ip_addr = ipOps.str_to_ip(ip)
        elseif address_type == 0x0f then
          lookup_response.ncacn_np = string.sub(data,pos,pos+address_len-2)
          floor_len = floor_len + address_len - 2
        elseif address_type == 0x10 then
          lookup_response.ncalrpc = string.sub(data,pos,pos+address_len-2)
          floor_len = floor_len + address_len - 2
        elseif address_type == 0x11 then
          lookup_response.netbios = string.sub(data,pos,pos+address_len-2)
          floor_len = floor_len + address_len - 2
        elseif address_type == 0x1f then
          lookup_response.ncacn_http, pos = string.unpack(">I2", data, pos)
        else
          stdnse.debug1("unknown address type %x",address_type)
        end
      end
    end
    pos = saved_pos + floor_len + 6
  end
  return status,lookup_response
end
function samr_PasswordProperties_tostr(val)
  return msrpctypes.samr_PasswordProperties_tostr(val)
end
function samr_AcctFlags_tostr(val)
  return msrpctypes.samr_AcctFlags_tostr(val)
end
function samr_connect4(smbstate, server)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling Connect4() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true)
  .. msrpctypes.marshall_int32(0x02)
  .. msrpctypes.marshall_samr_ConnectAccessMask("SAMR_ACCESS_ENUM_DOMAINS|SAMR_ACCESS_OPEN_DOMAIN")
  status, result = call_function(smbstate, 0x3E, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: Connect4() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['connect_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.connect4)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.connect4)"
  end
  return true, result
end
function samr_enumdomains(smbstate, connect_handle)
  local status, result
  local arguments
  local result
  local pos, align
  stdnse.debug2("MSRPC: Calling EnumDomains() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(connect_handle)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0x2000)
  status, result = call_function(smbstate, 0x06, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: EnumDomains() returned successfully")
  arguments = result['arguments']
  pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos)
  pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.enumdomains)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.enumdomains)"
  end
  return true, result
end
function samr_lookupdomain(smbstate, connect_handle, domain)
  local status, result
  local arguments
  local pos, align
  local referent_id
  stdnse.debug2("MSRPC: Calling LookupDomain(%s) [%s]", domain, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(connect_handle)
  .. msrpctypes.marshall_lsa_String(domain)
  status, result = call_function(smbstate, 0x05, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: LookupDomain() returned successfully")
  arguments = result['arguments']
  pos, result['sid'] = msrpctypes.unmarshall_dom_sid2_ptr(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.lookupdomain)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.lookupdomain)"
  end
  return true, result
end
function samr_opendomain(smbstate, connect_handle, sid)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenDomain(%s) [%s]", sid, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(connect_handle)
  .. msrpctypes.marshall_samr_DomainAccessMask("DOMAIN_ACCESS_LOOKUP_INFO_1|DOMAIN_ACCESS_LOOKUP_INFO_2|DOMAIN_ACCESS_ENUM_ACCOUNTS|DOMAIN_ACCESS_OPEN_ACCOUNT")
  .. msrpctypes.marshall_dom_sid2(sid)
  status, result = call_function(smbstate, 0x07, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenDomain() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['domain_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.opendomain)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.opendomain)"
  end
  return true, result
end
function samr_enumdomainusers(smbstate, domain_handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling EnumDomainUsers() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int32_ptr(nil)
  .. msrpctypes.marshall_samr_AcctFlags("ACB_NONE")
  .. msrpctypes.marshall_int32(0x0400)
  status, result = call_function(smbstate, 0x0d, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: EnumDomainUsers() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos)
  pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.enumdomainusers)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.enumdomainusers)"
  end
  return true, result
end
function samr_querydisplayinfo(smbstate, domain_handle, index, count)
  local status, result
  local arguments
  local pos, align
  if(count == nil) then
    count = 1
  end
  stdnse.debug2("MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int16(1) -- Level (1 = users, 3 = groups, 4 = usernames only)
  .. msrpctypes.marshall_int32(index)
  .. msrpctypes.marshall_int32(count)
  .. msrpctypes.marshall_int32(0x7FFFFFFF)
  status, result = call_function(smbstate, 0x28, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: QueryDisplayInfo() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['total_size'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['returned_size'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['info'] = msrpctypes.unmarshall_samr_DispInfo(arguments, pos)
  if(pos == nil) then
    return false, "SMB: An error occurred while calling unmarshall_samr_DispInfo"
  end
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.querydisplayall)"
  end
  if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_MORE_ENTRIES']) then
    return false, smb.get_status_name(result['return']) .. " (samr.querydisplayinfo)"
  end
  return true, result
end
function samr_querydomaininfo2(smbstate, domain_handle, level)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling QueryDomainInfo2(%d) [%s]", level, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int32(level)
  status, result = call_function(smbstate, 0x2e, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: QueryDomainInfo2() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['info'] = msrpctypes.unmarshall_samr_DomainInfo_ptr(arguments, pos)
  if(pos == nil) then
    return false, "unmarshall_samr_DomainInfo_ptr() returned an error"
  end
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.querydomaininfo2)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.querydomaininfo2)"
  end
  return true, result
end
function samr_enumdomainaliases(smbstate, domain_handle)
  local status, result
  local arguments
  local pos, align
  arguments = ''
  .. msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int32_ptr(nil)
  .. msrpctypes.marshall_int32(0x400)
  status, result = call_function(smbstate, 0x0f, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos = 1
  pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos)
  pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.enumdomainaliases)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.enumdomainaliases)"
  end
  return true, result
end
function samr_lookupnames(smbstate, domain_handle, names)
  local status, result
  local arguments
  local pos, align
  arguments = ''
  .. msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int32(#names)
  .. msrpctypes.marshall_lsa_String_array2(names)
  status, result = call_function(smbstate, 0x11, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos = 1
  pos, result['rids'] = msrpctypes.unmarshall_samr_Ids(arguments, pos)
  pos, result['types'] = msrpctypes.unmarshall_samr_Ids(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.lookupnames)"
  end
  if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then
    return false, "Couldn't find any names the host recognized"
  end
  if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then
    return false, smb.get_status_name(result['return']) .. " (samr.lookupnames)"
  end
  return true, result
end
function samr_openalias(smbstate, domain_handle, rid)
  local status, result
  local arguments
  local pos, align
  arguments = ''
  .. msrpctypes.marshall_policy_handle(domain_handle)
  .. msrpctypes.marshall_int32(0x0002000c) -- Full read permission
  .. msrpctypes.marshall_int32(rid)
  status, result = call_function(smbstate, 0x1b, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos = 1
  pos, result['alias_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.openalias)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.openalias)"
  end
  return true, result
end
function samr_getaliasmembership(smbstate, alias_handle,args)
  local status, result
  local arguments = alias_handle .. args
  status, result = call_function(smbstate, 0x10, arguments)
  if(status ~= true) then
    return false, result
  end
  return true, result
end
function samr_getmembersinalias(smbstate, alias_handle)
  local status, result
  local arguments
  local pos, align
  arguments = ''
  .. msrpctypes.marshall_policy_handle(alias_handle)
  status, result = call_function(smbstate, 0x21, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos = 1
  pos, result['sids'] = msrpctypes.unmarshall_lsa_SidArray(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.getmembersinalias)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)"
  end
  return true, result
end
function samr_close(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling Close() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x01, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: Close() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (samr.close)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (samr.close)"
  end
  return true, result
end
function lsa_openpolicy2(smbstate, server)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling LsarOpenPolicy2() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_lsa_ObjectAttribute()
  .. msrpctypes.marshall_int32(0x00000800)
  status, result = call_function(smbstate, 0x2C, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: LsarOpenPolicy2() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['policy_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (lsa.openpolicy2)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (lsa.openpolicy2)"
  end
  return true, result
end
function lsa_lookupnames2(smbstate, policy_handle, names)
  local status, result
  local arguments
  local result
  local pos, align
  stdnse.debug2("MSRPC: Calling LsarLookupNames2(%s) [%s]", stdnse.strjoin(", ", names), smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(policy_handle)
  .. msrpctypes.marshall_int32(#names)
  .. msrpctypes.marshall_lsa_String_array(names)
  .. msrpctypes.marshall_lsa_TransSidArray2({nil})
  .. msrpctypes.marshall_lsa_LookupNamesLevel("LOOKUP_NAMES_ALL")
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(2)
  status, result = call_function(smbstate, 0x3a, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: LsarLookupNames2() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos)
  pos, result['rids'] = msrpctypes.unmarshall_lsa_TransSidArray2(arguments, pos)
  pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (lsa.lookupnames2)"
  end
  if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then
    return false, "Couldn't find any names the host recognized"
  end
  if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then
    return false, smb.get_status_name(result['return']) .. " (lsa.lookupnames2)"
  end
  return true, result
end
function lsa_lookupsids2(smbstate, policy_handle, sids)
  local status, result
  local arguments
  local result
  local pos, align
  stdnse.debug2("MSRPC: Calling LsarLookupSids2(%s) [%s]", stdnse.strjoin(", ", sids), smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(policy_handle)
  .. msrpctypes.marshall_lsa_SidArray(sids)
  .. msrpctypes.marshall_lsa_TransNameArray2(nil)
  .. msrpctypes.marshall_int16(1)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_int32(2)
  status, result = call_function(smbstate, 0x39, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos)
  pos, result['names'] = msrpctypes.unmarshall_lsa_TransNameArray2(arguments, pos)
  local count
  pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (lsa.lookupnames2)"
  end
  if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED'] and result['return'] ~= smb.status_codes['NT_STATUS_NONE_MAPPED']) then
    return false, smb.get_status_name(result['return']) .. " (lsa.lookupsids2)"
  end
  stdnse.debug3("MSRPC: LsarLookupSids2(): Returning")
  return true, result
end
function lsa_close(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling LsaClose() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x00, arguments)
  if(status ~= true) then
    return false, result
  end
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (lsa.close)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (lsa.close)"
  end
  stdnse.debug3("MSRPC: LsaClose() returned successfully")
  return true, result
end
function lsa_SidType_tostr(val)
  return msrpctypes.lsa_SidType_tostr(val)
end
function winreg_openhku(smbstate)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenHKU() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_int16_ptr(0x1337, true)
  .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
  status, result = call_function(smbstate, 0x04, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenHKU() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.openhku)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.openhku)"
  end
  return true, result
end
function winreg_openhklm(smbstate)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenHKLM() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_int16_ptr(0x1337, true)
  .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
  status, result = call_function(smbstate, 0x02, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenHKLM() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.openhklm)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.openhklm)"
  end
  return true, result
end
function winreg_openhkpd(smbstate)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenHKPD() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_int16_ptr(0x1337, true)
  .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
  status, result = call_function(smbstate, 0x03, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenHKPD() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.openhkpd)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.openhkpd)"
  end
  return true, result
end
function winreg_openhkcu(smbstate)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenHKCU() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_int16_ptr(0x1337, true)
  .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
  status, result = call_function(smbstate, 0x01, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenHKCU() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.openhkcu)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.openhkcu)"
  end
  return true, result
end
function winreg_enumkey(smbstate, handle, index, name)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling EnumKey(%d) [%s]", index, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_int32(index)
  .. msrpctypes.marshall_winreg_StringBuf({name=""}, 520)
  .. msrpctypes.marshall_winreg_StringBuf_ptr({name=nil})
  .. msrpctypes.marshall_NTTIME_ptr(0)
  status, result = call_function(smbstate, 0x09, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: EnumKey() returned successfully")
  arguments = result['arguments']
  local referent_id
  pos = 1
  pos, result['name'] = msrpctypes.unmarshall_winreg_StringBuf(arguments, pos)
  pos, result['keyclass'] = msrpctypes.unmarshall_winreg_StringBuf_ptr(arguments, pos)
  pos, result['changed_time'] = msrpctypes.unmarshall_NTTIME_ptr(arguments, pos)
  result['changed_date'] = datetime.format_timestamp(result['changed_time'])
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.enumkey)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.enumkey)"
  end
  return true, result
end
function winreg_openkey(smbstate, handle, keyname)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenKey(%s) [%s]", keyname, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_winreg_String({name=keyname})
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS')
  status, result = call_function(smbstate, 0x0F, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenKey() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.openkey)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.openkey)"
  end
  return true, result
end
function winreg_queryinfokey(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling QueryInfoKey() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_winreg_String({name=""}, 2048)
  status, result = call_function(smbstate, 0x10, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: QueryInfoKey() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['classname'] = msrpctypes.unmarshall_winreg_String(arguments, pos)
  pos, result['subkeys'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['subkeylen'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['subkeysize'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['num_values'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['max_valnamelen'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['max_valbufsize'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['secdescsize'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['last_changed_time'] = msrpctypes.unmarshall_NTTIME(arguments, pos)
  result['last_changed_date'] = datetime.format_timestamp(result['last_changed_time'])
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.queryinfokey)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.queryinfokey)"
  end
  return true, result
end
function winreg_queryvalue(smbstate, handle, value)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling QueryValue(%s) [%s]", value, smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_winreg_String({name=value})
  .. msrpctypes.marshall_winreg_Type_ptr("REG_NONE")
  .. msrpctypes.marshall_int8_array_ptr("", 1000000)
  .. msrpctypes.marshall_int32_ptr(1000000)
  .. msrpctypes.marshall_int32_ptr(0)
  status, result = call_function(smbstate, 0x11, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: QueryValue() returned successfully")
  local length, referent_id
  arguments = result['arguments']
  pos = 1
  pos, result['type'] = msrpctypes.unmarshall_winreg_Type_ptr(arguments, pos)
  pos, result['data'] = msrpctypes.unmarshall_int8_array_ptr(arguments, pos)
  if(result['data'] ~= nil) then
    local _
    if(result['type'] == "REG_DWORD") then
      result['value'] = string.unpack("<I4", result['data'])
    elseif(result['type'] == "REG_SZ" or result['type'] == "REG_MULTI_SZ" or result['type'] == "REG_EXPAND_SZ") then
      _, result['value'] = msrpctypes.unicode_to_string(result['data'], 1, #result['data'] / 2)
    elseif(result['type'] == "REG_BINARY") then
      result['value'] = result['data']
    elseif(result['type'] == "REG_NONE") then
      result['value'] = ""
    else
      stdnse.debug1("MSRPC ERROR: Unknown type: %s", result['type'])
      result['value'] = result['type']
    end
  else
    result['value'] = nil
  end
  pos, result['size'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
  pos, result['length'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.queryvalue)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.queryvalue)"
  end
  return true, result
end
function winreg_closekey(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling CloseKey() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x05, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: CloseKey() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (winreg.closekey)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (winreg.closekey)"
  end
  return true, result
end
function svcctl_openscmanagera(smbstate, machinename)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenSCManagerA() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_ascii_ptr("\\\\" .. machinename)
  .. msrpctypes.marshall_ascii_ptr(nil)
  .. msrpctypes.marshall_int32(0x00000002)
  status, result = call_function(smbstate, 0x1b, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenSCManagerA() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.openscmanagera)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagera)"
  end
  return true, result
end
function svcctl_openscmanagerw(smbstate, machinename, access_mask)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenSCManagerW() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. machinename, true)
  .. msrpctypes.marshall_unicode_ptr(nil, true)
  .. msrpctypes.marshall_int32(access_mask)
  status, result = call_function(smbstate, 0x0f, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenSCManagerW() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.openscmanagerw)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagerw)"
  end
  return true, result
end
function svcctl_closeservicehandle(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling CloseServiceHandle() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x00, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenSCManagerA() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.closeservicehandle)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.closeservicehandle)"
  end
  return true, result
end
function svcctl_createservicew(smbstate, handle, service_name, display_name, path)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling CreateServiceW() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_unicode(service_name, true)
  .. msrpctypes.marshall_unicode_ptr(display_name, true)
  .. msrpctypes.marshall_int32(0x000f01ff) -- Access: Max
  .. msrpctypes.marshall_int32(0x00000010) -- Type: own process
  .. msrpctypes.marshall_int32(0x00000003) -- Start: Demand
  .. msrpctypes.marshall_int32(0x00000000) -- Error: Ignore
  .. msrpctypes.marshall_unicode(path, true)
  .. msrpctypes.marshall_unicode_ptr(nil)
  .. msrpctypes.marshall_int32_ptr(nil)
  .. msrpctypes.marshall_int8_ptr(nil)
  .. msrpctypes.marshall_int32(0)
  .. msrpctypes.marshall_unicode_ptr(nil)
  .. msrpctypes.marshall_int8_ptr(nil)
  .. msrpctypes.marshall_int32(0)
  status, result = call_function(smbstate, 0x0c, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: CreateServiceW() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['TagId'] = msrpctypes.unmarshall_int32_ptr(arguments, pos)
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.createservicew)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.createservicew)"
  end
  return true, result
end
function svcctl_deleteservice(smbstate, handle)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling DeleteService() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x02, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: DeleteService() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.deleteservice)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.deleteservice)"
  end
  return true, result
end
function svcctl_openservicew(smbstate, handle, name, access_mask)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling OpenServiceW() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_unicode(name, true)
  .. msrpctypes.marshall_int32(access_mask)
  status, result = call_function(smbstate, 0x10, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: OpenServiceW() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.openservicew)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.openservicew)"
  end
  return true, result
end
function svcctl_startservicew(smbstate, handle, args)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling StartServiceW() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. (args and msrpctypes.marshall_int32(#args) or msrpctypes.marshall_int32(0))
  .. msrpctypes.marshall_unicode_array_ptr(args, true)
  status, result = call_function(smbstate, 0x13, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: StartServiceW() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.startservicew)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.startservicew)"
  end
  return true, result
end
function svcctl_controlservice(smbstate, handle, control)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling ControlService() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_svcctl_ControlCode(control)
  status, result = call_function(smbstate, 0x01, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: ControlService() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.controlservice)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.controlservice)"
  end
  return true, result
end
function svcctl_queryservicestatus(smbstate, handle, control)
  local status, result
  local arguments
  local pos, align
  stdnse.debug2("MSRPC: Calling QueryServiceStatus() [%s]", smbstate['ip'])
  arguments = msrpctypes.marshall_policy_handle(handle)
  status, result = call_function(smbstate, 0x06, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: QueryServiceStatus() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (svcctl.queryservicestatus)"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (svcctl.queryservicestatus)"
  end
  return true, result
end
local function enumservicestatusparams(handle, tyepofservice, servicestate, cbbufsize, lpresumehandle)
  return msrpctypes.marshall_policy_handle(handle)
  .. msrpctypes.marshall_int32(tyepofservice, true)
  .. msrpctypes.marshall_int32(servicestate, true)
  .. msrpctypes.marshall_int32(cbbufsize, true)
  .. msrpctypes.marshall_int32_ptr(lpresumehandle, true)
end
local function unmarshall_enum_service_status(arguments, pos)
  local _
  local serviceNameOffset
  local displayNameOffset
  local serviceStatus
  local serviceName
  local displayName
  pos, serviceNameOffset = msrpctypes.unmarshall_int32(arguments, pos)
  pos, displayNameOffset = msrpctypes.unmarshall_int32(arguments, pos)
  pos, serviceStatus = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos)
  _, serviceName = msrpctypes.unmarshall_lptstr(arguments, serviceNameOffset + 5)
  _, displayName = msrpctypes.unmarshall_lptstr(arguments, displayNameOffset + 5)
  serviceName = unicode.utf16to8(serviceName)
  displayName = unicode.utf16to8(displayName)
  serviceName = string.sub(serviceName, 1, serviceName:len()-1)
  displayName = string.sub(displayName, 1, displayName:len()-1)
  stdnse.debug2("ServiceName = %s", serviceName)
  stdnse.debug2("DisplayName = %s", displayName)
  return pos, serviceName, displayName, serviceStatus
end
function svcctl_enumservicesstatusw(smbstate, handle, dwservicetype, dwservicestate)
  local status
  local result
  local arguments
  local pos
  local _
  local serviceName
  local displayName
  local serviceStatus
  local lpservices
  local output = stdnse.output_table()
  local DW_SERVICE_TYPE = dwservicetype or 0x00000010
  local DW_SERVICE_STATE = dwservicestate or 0x00000003
  arguments = enumservicestatusparams(handle, DW_SERVICE_TYPE, DW_SERVICE_STATE, 0x00, nil)
  status, result = call_function(smbstate, 0x0e, arguments)
  if status ~= true then
    return false, result
  end
  arguments = result["arguments"]
  pos = 1
  lpservices, pos = string.unpack("<s4", arguments, pos)
  pos, result["pcbBytesNeeded"] = msrpctypes.unmarshall_int32(arguments, pos)
  _, result["ReturnValue"] = msrpctypes.unmarshall_int32(arguments, arguments:len()-3)
  if result["ReturnValue"] == 0x00 then
    return true, {}
  elseif result["ReturnValue"] == 0x05 then
    return false, "Access is denied."
  elseif not (result["ReturnValue"] == 0x7A or result["ReturnValue"] == 0xEA) then
    return false, "Error occurred. Error code = " .. tostring(result["ReturnValue"])
  end
  local MAX_BUFFER_SIZE = 0xfa00
  stdnse.debug3("MAX_BUFFER_SIZE = %d", MAX_BUFFER_SIZE)
  result["lpResumeHandle"] = 0x00
  repeat
    arguments = enumservicestatusparams(handle, DW_SERVICE_TYPE, DW_SERVICE_STATE, math.min(result["pcbBytesNeeded"], MAX_BUFFER_SIZE), result["lpResumeHandle"])
    status, result = call_function(smbstate, 0x0e, arguments)
    if status ~= true then
      return false, result
    end
    arguments = result["arguments"]
    local length = arguments:len()
    _, result["ReturnValue"] = msrpctypes.unmarshall_int32(arguments, length - 3)
    stdnse.debug("ReturnValue = %d", result["ReturnValue"])
    _, result["lpResumeHandle"] = msrpctypes.unmarshall_int32_ptr(arguments, length - 11)
    stdnse.debug("lpResumeHandle = %d", result["lpResumeHandle"])
    _, result["lpServicesReturned"] = msrpctypes.unmarshall_int32(arguments, length - 15)
    stdnse.debug("lpServicesReturned = %d", result["lpServicesReturned"])
    _, result["pcbBytesNeeded"] = msrpctypes.unmarshall_int32(arguments, length - 19)
    stdnse.debug("pcbBytesNeeded = %d", result["pcbBytesNeeded"])
    pos = 5
    local count = result["lpServicesReturned"]
    repeat
      pos, serviceName, displayName, serviceStatus = unmarshall_enum_service_status(arguments, pos)
      local t = stdnse.output_table()
      t["display_name"] = displayName
      t["state"] = serviceStatus["state"]
      t["type"] = serviceStatus["type"]
      t["controls_accepted"] = serviceStatus["controls_accepted"]
      output[serviceName] = t
      count = count - 1
    until count < 1
  until result["pcbBytesNeeded"] == 0
  stdnse.debug3("MSRPC: EnumServiceStatus() returned successfully")
  return true, output
end
function atsvc_jobadd(smbstate, server, command, time)
  local status, result
  local arguments
  local pos, align
  if(time == nil) then
  end
  stdnse.debug2("MSRPC: Calling AddJob(%s) [%s]", command, smbstate['ip'])
  arguments = msrpctypes.marshall_unicode_ptr(server, true)
  .. msrpctypes.marshall_atsvc_JobInfo(command, time)
  status, result = call_function(smbstate, 0x00, arguments)
  if(status ~= true) then
    return false, result
  end
  stdnse.debug3("MSRPC: AddJob() returned successfully")
  arguments = result['arguments']
  pos = 1
  pos, result['job_id'] = msrpctypes.unmarshall_int32(arguments, pos)
  pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos)
  if(result['return'] == nil) then
    return false, "Read off the end of the packet (atsvc.addjob())"
  end
  if(result['return'] ~= 0) then
    return false, smb.get_status_name(result['return']) .. " (atsvc.addjob())"
  end
  return true, result
end
function samr_enum_users(host)
  local smbstate
  local bind_result, connect4_result, enumdomains_result
  local connect_handle
  local status, smbstate
  local response = {}
  status, smbstate = start_smb(host, SAMR_PATH, true)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  status, connect4_result = samr_connect4(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, connect4_result
  end
  connect_handle = connect4_result['connect_handle']
  status, enumdomains_result = samr_enumdomains(smbstate, connect_handle)
  if(status == false) then
    stop_smb(smbstate)
    return false, enumdomains_result
  end
  if(#enumdomains_result['sam']['entries'] == 0) then
    stop_smb(smbstate)
    return false, "Couldn't find any domains"
  end
  for i = 1, #enumdomains_result['sam']['entries'], 1 do
    local domain = enumdomains_result['sam']['entries'][i]['name']
    if(domain ~= 'Builtin') then
      local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain)
      if(status == false) then
        stop_smb(smbstate)
        return false, lookupdomain_result
      end
      local sid = lookupdomain_result['sid']
      local status, opendomain_result = samr_opendomain(smbstate, connect_handle, sid)
      if(status == false) then
        stop_smb(smbstate)
        return false, opendomain_result
      end
      local domain_handle = opendomain_result['domain_handle']
      local j = 0
      repeat
        local status, querydisplayinfo_result = samr_querydisplayinfo(smbstate, domain_handle, j, SAMR_GROUPSIZE)
        if(status == false) then
          stop_smb(smbstate)
          return false, querydisplayinfo_result
        end
        if(querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil) then
          local k
          for k = 1, #querydisplayinfo_result['info']['entries'], 1 do
            local array = {}
            local l
            array['name']        = querydisplayinfo_result['info']['entries'][k]['account_name']
            array['fullname']    = querydisplayinfo_result['info']['entries'][k]['full_name']
            array['description'] = querydisplayinfo_result['info']['entries'][k]['description']
            array['rid']         = querydisplayinfo_result['info']['entries'][k]['rid']
            array['domain']      = domain
            array['type']        = 'SID_NAME_USER'
            array['typestr']     = 'User'
            array['source']      = 'SAMR Enumeration'
            array['flags']       = querydisplayinfo_result['info']['entries'][k]['acct_flags']
            for l = 1, #array['flags'], 1 do
              array['flags'][l] = samr_AcctFlags_tostr(array['flags'][l])
            end
            response[#response + 1] = array
          end
        end
        j = j + SAMR_GROUPSIZE
      until querydisplayinfo_result['return'] == 0
      samr_close(smbstate, domain_handle)
    end -- Checking for 'builtin'
  end -- Domain loop
  samr_close(smbstate, connect_handle)
  stop_smb(smbstate)
  return true, response
end
function samr_enum_groups(host)
  stdnse.debug1("MSRPC: Attempting to enumerate groups on %s", host.ip)
  local status, smbstate = start_smb(host, SAMR_PATH, true)
  if(status == false) then
    return false, smbstate
  end
  local status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  local status, connect4_result = samr_connect4(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, connect4_result
  end
  local connect_handle = connect4_result['connect_handle']
  local status, enumdomains_result = samr_enumdomains(smbstate, connect_handle)
  if(status == false) then
    stop_smb(smbstate)
    return false, enumdomains_result
  end
  if(#enumdomains_result['sam']['entries'] == 0) then
    stop_smb(smbstate)
    return false, "Couldn't find any domains"
  end
  local domains = {}
  for _, domain in ipairs(enumdomains_result['sam']['entries']) do
    domain = domain['name']
    domains[domain] = {}
    local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain)
    if(status == false) then
      stop_smb(smbstate)
      return false, lookupdomain_result
    end
    local domain_sid = lookupdomain_result['sid']
    local status, opendomain_result = samr_opendomain(smbstate, connect_handle, domain_sid)
    if(status == false) then
      stop_smb(smbstate)
      return false, opendomain_result
    end
    local domain_handle = opendomain_result['domain_handle']
    local status, enumaliases_result = samr_enumdomainaliases(smbstate, domain_handle)
    if(status == false) then
      stop_smb(smbstate)
      return false, "Couldn't enumerate groups: " .. enumaliases_result
    end
    if(enumaliases_result['sam'] == nil or enumaliases_result['sam']['entries'] == nil) then
      return false, "ERROR: No groups returned by samr_EnumDomainAliases()"
    end
    stdnse.debug1("MSRPC: Found %d groups in %s", #enumaliases_result['sam']['entries'], domain)
    local group_rids = {}
    for _, group in ipairs(enumaliases_result['sam']['entries']) do
      local group_rid = group['idx']
      table.insert(group_rids, group_rid)
      domains[domain][group_rid] = {}
      domains[domain][group_rid]['name'] = group['name']
    end -- Loop over group entries
    for _, group_rid in ipairs(group_rids) do
      local status, openalias_result = samr_openalias(smbstate, domain_handle, group_rid)
      if(not(status)) then
        stop_smb(smbstate)
        return false, "Couldn't open handle to group: " .. openalias_result
      end
      local group_handle = openalias_result['alias_handle']
      local status, getmembers_result = samr_getmembersinalias(smbstate, group_handle)
      if(not(status)) then
        stop_smb(smbstate)
        return false, "Couldn't get members in group: " .. getmembers_result
      end
      local member_sids = {}
      if(getmembers_result and getmembers_result.sids and getmembers_result.sids.sids) then
        member_sids = getmembers_result.sids.sids
      end
      stdnse.debug1("MSRPC: Adding group '%s' (RID: %d) with %d members", domains[domain][group_rid]['name'], group_rid, #member_sids)
      domains[domain][group_rid]['member_sids'] = member_sids
      samr_close(smbstate, group_handle)
    end -- Loop over group RIDs
    samr_close(smbstate, domain_handle)
  end -- Domain loop
  samr_close(smbstate, connect_handle)
  stop_smb(smbstate)
  local status, smbstate = start_smb(host, LSA_PATH, true)
  if(status == false) then
    return false, smbstate
  end
  local status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  local status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, openpolicy2_result
  end
  for domain, domain_data in pairs(domains) do
    for group_rid, group in pairs(domain_data) do
      local status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], group['member_sids'])
      if(status == false) then
        stop_smb(smbstate)
        return false, "Error looking up RIDs: " .. lookupsids2_result
      end
      if(lookupsids2_result and lookupsids2_result.names and lookupsids2_result.names.names and (#lookupsids2_result.names.names > 0)) then
        local members = {}
        for _, resolved_name in ipairs(lookupsids2_result.names.names) do
          if(resolved_name.sid_type == "SID_NAME_USER") then
            table.insert(members, resolved_name.name)
          end
        end
        domains[domain][group_rid]['members'] = members
      else
        domains[domain][group_rid]['members'] = {}
      end
    end
  end
  lsa_close(smbstate, openpolicy2_result['policy_handle'])
  stop_smb(smbstate)
  return true, domains
end
function lsa_enum_users(host)
  local smbstate
  local response = {}
  local status, smbstate, bind_result, openpolicy2_result, lookupnames2_result, lookupsids2_result
  status, smbstate = start_smb(host, LSA_PATH, true)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, openpolicy2_result
  end
  local names = {"administrator", "guest", "test"}
  if(smbstate['domain'] ~= nil) then
    names[#names + 1] = smbstate['domain']
  end
  if(smbstate['server'] ~= nil) then
    names[#names + 1] = string.sub(smbstate['server'], 1, #smbstate['server'] - 1)
  end
  local result, server_name = netbios.get_server_name(host.ip)
  if(result == true) then
    names[#names + 1] = server_name
  end
  local result, user_name = netbios.get_user_name(host.ip)
  if(result == true) then
    names[#names + 1] = user_name
  end
  status, lookupnames2_result = lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
  if(status == false) then
    stop_smb(smbstate)
    return false, lookupnames2_result
  end
  for i = 1, #lookupnames2_result['domains']['domains'], 1 do
    local domain = lookupnames2_result['domains']['domains'][i]['name']
    local sid = lookupnames2_result['domains']['domains'][i]['sid']
    local sids   = { }
    for j = 500, 500 + LSA_GROUPSIZE, 1 do
      sids[#sids + 1] = sid .. "-" .. j
    end
    status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
    if(status == false) then
      stdnse.debug1("Error looking up RIDs: %s", lookupsids2_result)
    else
      for j = 1, #lookupsids2_result['names']['names'], 1 do
        if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then
          local result = {}
          result['name']    = lookupsids2_result['names']['names'][j]['name']
          result['rid'] = 500 + j - 1
          result['domain']  = domain
          result['type']    = lookupsids2_result['names']['names'][j]['sid_type']
          result['typestr'] = lsa_SidType_tostr(result['type'])
          result['source']  = "LSA Bruteforce"
          table.insert(response, result)
        end
      end
    end
    local start      = 1000
    local empty      = 0
    repeat
      local used_names = 0
      local sids = {}
      for j = start, start + LSA_GROUPSIZE, 1 do
        sids[#sids + 1] = sid .. "-" .. j
      end
      status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids)
      if(status == false) then
        stdnse.debug1("Error looking up RIDs: %s", lookupsids2_result)
      else
        for j = 1, #lookupsids2_result['names']['names'], 1 do
          local name = lookupsids2_result['names']['names'][j]['name']
          local rid = start + j - 1
          local typenum = lookupsids2_result['names']['names'][j]['sid_type']
          local typestr = lsa_SidType_tostr(typenum)
          if(tonumber(name) ~= rid) then
            if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then
              local result = {}
              result['name']    = name
              result['rid'] = rid
              result['domain']  = domain
              result['type']    = typenum
              result['typestr'] = typestr
              result['source']  = "LSA Bruteforce"
              table.insert(response, result)
              used_names = used_names + 1
            end
          end
        end
      end
      if(used_names == 0) then
        empty = empty + 1
      else
        empty = 0
      end
      start = start + LSA_GROUPSIZE
    until (status == false or (empty == LSA_MINEMPTY))
  end
  lsa_close(smbstate, openpolicy2_result['policy_handle'])
  stop_smb(smbstate)
  return true, response
end
function get_user_list(host)
  local status_samr, result_samr
  local status_lsa,  result_lsa
  local response = {}
  local names = {}
  status_lsa,  result_lsa  = lsa_enum_users(host)
  if(status_lsa == false) then
    stdnse.debug1("MSRPC: Failed to enumerate users through LSA: %s", result_lsa)
  else
    for i = 1, #result_lsa, 1 do
      if(result_lsa[i]['name'] ~= nil and result_lsa[i]['type'] == "SID_NAME_USER") then
        response[result_lsa[i]['domain'] .. '\\' .. result_lsa[i]['name']] = result_lsa[i]
      end
    end
  end
  status_samr, result_samr = samr_enum_users(host)
  if(status_samr == false) then
    stdnse.debug1("MSRPC: Failed to enumerate users through SAMR: %s", result_samr)
  else
    for i = 1, #result_samr, 1 do
      if(result_samr[i]['name'] ~= nil and result_samr[i]['type'] == "SID_NAME_USER") then
        response[result_samr[i]['domain'] .. '\\' .. result_samr[i]['name']] = result_samr[i]
      end
    end
  end
  if(status_samr == false and status_lsa == false) then
    return false, "MSRPC: Couldn't enumerate users; see debug output for more information"
  end
  for i, v in pairs(response) do
    table.insert(names, i)
  end
  table.sort(names, function(a,b) return a:lower() < b:lower() end )
  return true, response, names
end
local function get_domain_info(host, domain)
  local result = {}
  local status, smbstate, bind_result, connect4_result, lookupdomain_result, opendomain_result, enumdomainusers_result
  status, smbstate  = start_smb(host, SAMR_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  status, connect4_result = samr_connect4(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, connect4_result
  end
  status, lookupdomain_result = samr_lookupdomain(smbstate, connect4_result['connect_handle'], domain)
  if(status == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, "Couldn't look up the domain: " .. lookupdomain_result
  end
  status, opendomain_result = samr_opendomain(smbstate, connect4_result['connect_handle'], lookupdomain_result['sid'])
  if(status == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, opendomain_result
  end
  local status_1,  querydomaininfo2_result_1  = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 1)
  local status_8,  querydomaininfo2_result_8  = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 8)
  local status_12, querydomaininfo2_result_12 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 12)
  if(status_1 == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, querydomaininfo2_result_1
  end
  if(status_8 == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, querydomaininfo2_result_8
  end
  if(status_12 == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, querydomaininfo2_result_12
  end
  status, enumdomainusers_result = samr_enumdomainusers(smbstate, opendomain_result['domain_handle'])
  if(status == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, enumdomainusers_result
  end
  local status, enumdomaingroups_result = samr_enumdomainaliases(smbstate, opendomain_result['domain_handle'])
  if(status == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, enumdomaingroups_result
  end
  samr_close(smbstate, opendomain_result['domain_handle'])
  stop_smb(smbstate)
  local groups = {}
  if(enumdomaingroups_result['sam'] ~= nil and enumdomaingroups_result['sam']['entries'] ~= nil) then
    for _, group in ipairs(enumdomaingroups_result['sam']['entries']) do
      table.insert(groups, group.name)
    end
  end
  local names = {}
  if(enumdomainusers_result['sam'] ~= nil and enumdomainusers_result['sam']['entries'] ~= nil) then
    for _, name in ipairs(enumdomainusers_result['sam']['entries']) do
      table.insert(names, name.name)
    end
  end
  local response = {}
  response['name'] = domain
  response['sid']  = lookupdomain_result['sid']
  response['groups'] = groups
  response['users'] = names
  if(querydomaininfo2_result_8['info']['domain_create_time'] ~= 0) then
    response['created'] = datetime.format_timestamp(querydomaininfo2_result_8['info']['domain_create_time'])
  else
    response['created'] = "unknown"
  end
  response['min_password_length'] = querydomaininfo2_result_1['info']['min_password_length']
  response['max_password_age']    = querydomaininfo2_result_1['info']['max_password_age'] / 60 / 60 / 24
  response['min_password_age']    = querydomaininfo2_result_1['info']['min_password_age'] / 60 / 60 / 24
  response['password_history']    = querydomaininfo2_result_1['info']['password_history_length']
  response['lockout_duration']    = querydomaininfo2_result_12['info']['lockout_duration'] / 60
  response['lockout_threshold']   = querydomaininfo2_result_12['info']['lockout_threshold']
  response['lockout_window']      = querydomaininfo2_result_12['info']['lockout_window'] / 60
  if(response['min_password_length'] <= 0) then
    response['min_password_length'] = nil
  end
  if(response['max_password_age'] < 0 or response['max_password_age'] > 5000) then
    response['max_password_age'] = nil
  end
  if(response['min_password_age'] <= 0) then
    response['min_password_age'] = nil
  end
  if(response['password_history'] <= 0) then
    response['password_history'] = nil
  end
  if(response['lockout_duration'] <= 0) then
    response['lockout_duration'] = nil
  end
  if(response['lockout_threshold'] <= 0) then
    response['lockout_threshold'] = nil
  end
  if(response['lockout_window'] <= 0) then
    response['lockout_window'] = nil
  end
  local password_properties = querydomaininfo2_result_1['info']['password_properties']
  if(#password_properties > 0) then
    local password_properties_response = {}
    password_properties_response['name'] = "Password properties:"
    for j = 1, #password_properties, 1 do
      table.insert(password_properties_response, samr_PasswordProperties_tostr(password_properties[j]))
    end
    response['password_properties'] = password_properties_response
  end
  return true, response
end
function get_domains(host)
  local result = {}
  local status, smbstate, bind_result, connect4_result, enumdomains_result
  status, smbstate  = start_smb(host, SAMR_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil)
  if(status == false) then
    stop_smb(smbstate)
    return false, bind_result
  end
  status, connect4_result = samr_connect4(smbstate, host.ip)
  if(status == false) then
    stop_smb(smbstate)
    return false, connect4_result
  end
  status, enumdomains_result = samr_enumdomains(smbstate, connect4_result['connect_handle'])
  if(status == false) then
    samr_close(smbstate, connect4_result['connect_handle'])
    stop_smb(smbstate)
    return false, enumdomains_result
  end
  samr_close(smbstate, connect4_result['connect_handle'])
  stop_smb(smbstate)
  if(#enumdomains_result['sam']['entries'] == 0) then
    return false, "No domains could be found"
  end
  local response = {}
  for i = 1, #enumdomains_result['sam']['entries'], 1 do
    local domain = enumdomains_result['sam']['entries'][i]['name']
    local status, domain_info = get_domain_info(host, domain)
    if(not(status)) then
      return false, "Couldn't get info for the domain: " .. domain_info
    else
      response[domain] = domain_info
    end
  end
  return true, response
end
function service_create(host, servicename, path)
  local status, smbstate, bind_result, open_result, create_result, close_result
  stdnse.debug1("Creating service: %s (%s)", servicename, path)
  status, smbstate = start_smb(host, SVCCTL_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  stdnse.debug2("Opening the remote service manager")
  status, open_result = svcctl_openscmanagerw(smbstate, host.ip, 0x02000000)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_result
  end
  stdnse.debug2("Creating the service")
  status, create_result = svcctl_createservicew(smbstate, open_result['handle'], servicename, servicename, path)
  if(status == false) then
    smb.stop(smbstate)
    return false, create_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, create_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  smb.stop(smbstate)
  return true
end
function service_start(host, servicename, args)
  local status, smbstate, bind_result, open_result, open_service_result, start_result, close_result, query_result
  stdnse.debug1("Starting service: %s", servicename)
  status, smbstate = start_smb(host, SVCCTL_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  stdnse.debug1("Opening the remote service manager")
  status, open_result = svcctl_openscmanagerw(smbstate, host.ip, 0x02000000)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_result
  end
  stdnse.debug2("Getting a handle to the service")
  status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename, 0x000f01ff)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_service_result
  end
  stdnse.debug2("Starting the service")
  status, start_result = svcctl_startservicew(smbstate, open_service_result['handle'], args)
  if(status == false) then
    smb.stop(smbstate)
    return false, start_result
  end
  stdnse.debug1("Waiting for the service to start")
  repeat
    status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle'])
    if(status == false) then
      smb.stop(smbstate)
      return false, query_result
    end
    stdnse.sleep(.5)
  until query_result['service_status']['controls_accepted'][1] == "SERVICE_CONTROL_STOP" or query_result['service_status']['state'][1] == "SERVICE_STATE_ACTIVE"
  status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  smb.stop(smbstate)
  return true
end
function service_stop(host, servicename)
  local status, smbstate, bind_result, open_result, open_service_result, control_result, close_result, query_result
  stdnse.debug1("Stopping service: %s", servicename)
  status, smbstate = start_smb(host, SVCCTL_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  stdnse.debug2("Opening the remote service manager")
  status, open_result = svcctl_openscmanagerw(smbstate, host.ip, 0x02000000)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_result
  end
  stdnse.debug2("Getting a handle to the service")
  status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename, 0x000f01ff)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_service_result
  end
  stdnse.debug2("Stopping the service")
  status, control_result = svcctl_controlservice(smbstate, open_service_result['handle'], "SERVICE_CONTROL_STOP")
  if(status == false) then
    smb.stop(smbstate)
    return false, control_result
  end
  stdnse.debug2("Waiting for the service to stop")
  repeat
    status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle'])
    if(status == false) then
      smb.stop(smbstate)
      return false, query_result
    end
    stdnse.sleep(.5)
  until query_result['service_status']['controls_accepted'][1] == nil
  status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  smb.stop(smbstate)
  return true
end
function service_delete(host, servicename)
  local status, smbstate, bind_result, open_result, open_service_result, delete_result, close_result
  stdnse.debug1("Deleting service: %s", servicename)
  status, smbstate = start_smb(host, SVCCTL_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  stdnse.debug2("Opening the remote service manager")
  status, open_result = svcctl_openscmanagerw(smbstate, host.ip, 0x02000000)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_result
  end
  stdnse.debug2("Getting a handle to the service: %s", servicename)
  status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename, 0x000f01ff)
  if(status == false) then
    smb.stop(smbstate)
    return false, open_service_result
  end
  stdnse.debug2("Deleting the service")
  status, delete_result = svcctl_deleteservice(smbstate, open_service_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, delete_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle'])
  if(status == false) then
    smb.stop(smbstate)
    return false, close_result
  end
  smb.stop(smbstate)
  return true
end
function get_server_stats(host)
  local stats
  local status
  local smbstate
  status, smbstate = start_smb(host, SRVSVC_PATH)
  if(status == false) then
    return false, smbstate
  end
  local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  local status, netservergetstatistics_result = srvsvc_netservergetstatistics(smbstate, host.ip)
  if(status == false) then
    smb.stop(smbstate)
    return false, netservergetstatistics_result
  end
  smb.stop(smbstate)
  local stats = netservergetstatistics_result['stat']
  stats['start_str'] = datetime.format_timestamp(stats['start'])
  stats['period'] = os.time() - stats['start']
  stats.period_str = datetime.format_time(stats.period)
  stats['bytessent'] = ((stats['bytessent_high'] << 32) | stats['bytessent_low'])
  stats['bytesrcvd'] = ((stats['bytesrcvd_high'] << 32) | stats['bytesrcvd_low'])
  if(stats['period'] == 0) then
    stats['period'] = 1
  end
  stats['bytessentpersecond'] = stats['bytessent'] / stats['period']
  stats['bytesrcvdpersecond'] = stats['bytesrcvd'] / stats['period']
  return true, stats
end
function enum_shares(host)
  local status, smbstate
  local bind_result, netshareenumall_result
  local shares
  status, smbstate = start_smb(host, SRVSVC_PATH)
  if(status == false) then
    return false, smbstate
  end
  status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  status, netshareenumall_result = srvsvc_netshareenumall(smbstate, host.ip)
  if(status == false) then
    smb.stop(smbstate)
    return false, netshareenumall_result
  end
  smb.stop(smbstate)
  shares = {}
  for i, v in pairs(netshareenumall_result['ctr']['array']) do
    shares[#shares + 1] = v['name']
  end
  return true, shares
end
function get_share_info(host, name)
  local response = {}
  local status, smbstate = start_smb(host, SRVSVC_PATH)
  if(status == false) then
    return false, smbstate
  end
  local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil)
  if(status == false) then
    smb.stop(smbstate)
    return false, bind_result
  end
  local status, netsharegetinfo_result = srvsvc_netsharegetinfo(smbstate, host.ip, name, 2)
  stdnse.debug2("NetShareGetInfo status:%s result:%s", status, netsharegetinfo_result)
  if(status == false) then
    if(string.find(netsharegetinfo_result, "NT_STATUS_WERR_ACCESS_DENIED")) then
      stdnse.debug2("Calling NetShareGetInfo with information level 1")
      status, netsharegetinfo_result = srvsvc_netsharegetinfo(smbstate, host.ip, name, 1)
      if status then
        smb.stop(smbstate)
        return true, netsharegetinfo_result
      end
    end
    smb.stop(smbstate)
    return false, netsharegetinfo_result
  end
  smb.stop(smbstate)
  return true, netsharegetinfo_result
end
ROUTER_PATH = "\\router" --also can be reached across "\\srvsvc" pipe in WinXP
RASRPC_UUID = "\x36\x00\x61\x20\x22\xfa\xcf\x11\x98\x23\x00\xa0\xc9\x11\xe5\xdf"
RASRPC_VERSION = 1
RRAS_RegTypes = {}
RRAS_RegTypes['PORTENUM'] = 21
RRAS_RegTypes['GETINFO'] = 22
RRAS_RegTypes['GETDEVCONFIG'] = 73 --this method is vulnerable to ms06-025
RRAS_RegTypes['SETDEVICECONFIGINFO'] = 94
RRAS_RegTypes['GETDEVICECONFIGINFO'] = 95
RRAS_RegTypes['GETCALLEDID'] = 105
RRAS_RegTypes['SETCALLEDID'] = 106
RRAS_RegTypes['GETNDISWANDRIVERCAPS'] = 111
function RRAS_marshall_RequestBuffer(RB_PCBIndex, RB_ReqType, RB_Buffer)
  local rb_blob, RB_Dummy, RB_Done, Alignment
  RB_Dummy = 4
  RB_Done = 0
  Alignment = 0
  rb_blob = string.pack("<I4I4I4I4I8",
    RB_PCBIndex,
    RB_ReqType,
    RB_Dummy,
    RB_Done,
    Alignment)
  .. RB_Buffer
  return rb_blob
end
local RRAS_DEBUG_LVL = 2 --debug level for rras operations when calling stdnse.debug
RRAS_Opnums = {}
RRAS_Opnums["RasRpcDeleteEntry"] = 5
RRAS_Opnums["RasRpcGetUserPreferences"] = 9
RRAS_Opnums["RasRpcSetUserPreferences"] = 10
RRAS_Opnums["RasRpcGetSystemDirectory"] = 11
RRAS_Opnums["RasRpcSubmitRequest"] = 12
RRAS_Opnums["RasRpcGetInstalledProtocolsEx"] = 14
RRAS_Opnums["RasRpcGetVersion"] = 15
function RRAS_SubmitRequest(smbstate, pReqBuffer, dwcbBufSize)
  if(dwcbBufSize == nil) then
    dwcbBufSize = #pReqBuffer
  end
  local req_blob
  --[in, out, unique, size_is(dwcbBufSize) PBYTE pReqBuffer,
  req_blob = string.pack("<I4I4", 0x20000, dwcbBufSize) .. pReqBuffer .. get_pad(pReqBuffer,4) --unique pointer see samba:ndr_push_unique_ptr
  --[in] DWORD dwcbBufSize
  .. msrpctypes.marshall_int32(dwcbBufSize)
  local status, result
  stdnse.debug(
    RRAS_DEBUG_LVL,
    "RRAS_SubmitRequest: Calling...")
  status, result = call_function(
  smbstate,
  RRAS_Opnums["RasRpcSubmitRequest"],
  req_blob)
  if(status == false) then
    stdnse.debug(
      RRAS_DEBUG_LVL,
      "RRAS_SubmitRequest: Call function failed: %s",
    result)
    return false, result
  end
  stdnse.debug(
    RRAS_DEBUG_LVL,
    "RRAS_SubmitRequest: Returned successfully")
  local rep_blob
  rep_blob = result
  return true, rep_blob
end
DNSSERVER_UUID_STR = "50abc2a4-574d-40b3-9d66-ee4fd5fba076"
DNSSERVER_UUID = "\xa4\xc2\xab\x50\x4d\x57\xb3\x40\x9d\x66\xee\x4f\xd5\xfb\xa0\x76"
DNSSERVER_PATH = "\\DNSSERVER"
DNSSERVER_VERSION = 5
DNSSERVER_ConfInfo =
{
  DNSSERVER_IntProp = {},
  DNSSERVER_AddrArrProp = {},
  DNSSERVER_StrProp = {},
  DNSSERVER_StrLstProp = {}
}
local DNSSERVER_DEBUG_LVL = 2 --debug level for dnsserver operations when calling stdnse.debug
DNSSERVER_Opnums = {}
DNSSERVER_Opnums['R_DnssrvOperation'] = 0
DNSSERVER_Opnums['R_DnssrvQuery'] = 1
DNSSERVER_Opnums['R_DnssrvComplexOperation'] = 2
DNSSERVER_Opnums['R_DnssrvEnumRecords'] = 3
DNSSERVER_Opnums['R_DnssrvUpdateRecord'] = 4
DNSSERVER_Opnums['R_DnssrvOperation2'] = 5
DNSSERVER_Opnums['R_DnssrvQuery2'] = 6
DNSSERVER_Opnums['R_DnssrvComplexOperation2'] = 7
DNSSERVER_Opnums['R_DnssrvEnumRecords2'] = 8
DNSSERVER_Opnums['R_DnssrvUpdateRecord2'] = 9
--[[
LONG R_DnssrvQuery(
  [in, unique, string] LPCWSTR pwszServerName,
  [in, unique, string] LPCSTR pszZone,
  [in, unique, string] LPCSTR pszOperation,
  [out] PDWORD pdwTypeId,
  [out, switch_is(*pdwTypeId)] DNSSRV_RPC_UNION* ppData);
--]]
function DNSSERVER_Query(smbstate, server_name, zone, operation)
  local status
  local req_blob, srv_name_utf16, zone_ascii, operation_ascii
  --[in, unique, string] LPCWSTR pwszServerName,
  local unique_ptr
  unique_ptr = 0x00020000
  srv_name_utf16 = msrpctypes.string_to_unicode(server_name, true)
  req_blob = string.pack("<I4I4I4I4",
    unique_ptr,
    #srv_name_utf16/2,
    0,
    #srv_name_utf16/2)
  .. srv_name_utf16
  .. get_pad(srv_name_utf16, 4)
  --[in, unique, string] LPCSTR pszZone,
  if(zone == nil) then
    req_blob = string.pack("<I4", 0x00000000)
  else
    zone_ascii = zone .. '\0'
    req_blob = req_blob .. string.pack("<I4I4I4I4",
      unique_ptr + 1,
      #zone_ascii,
      0,
      #zone_ascii)
    .. zone_ascii
    .. get_pad(zone_ascii, 4)
  end
  --[in, unique, string] LPCSTR pszOperation,
  operation_ascii = operation .. '\0'
  req_blob = req_blob .. string.pack("<I4I4I4I4",
    unique_ptr+2,
    #operation_ascii,
    0,
    #operation_ascii)
  .. operation_ascii
  .. get_pad(operation_ascii, 4)
  local call_result
  stdnse.debug(
    DNSSERVER_DEBUG_LVL,
    "DNSSERVER_Query: Calling...")
  status, call_result = call_function(
    smbstate,
    DNSSERVER_Opnums['R_DnssrvQuery'],
    req_blob)
  if(status == false) then
    stdnse.debug(
      DNSSERVER_DEBUG_LVL,
      "DNSSERVER_Query: Call function failed: %s",
      call_result)
    return false, call_result
  end
  stdnse.debug(
    DNSSERVER_DEBUG_LVL,
    "DNSSERVER_Query: Returned successfully")
  local rep_blob, pos, ptr, result
  rep_blob = call_result['arguments']
  --[out] PDWORD pdwTypeId,
  result = {}
  pos, result['type_id'] = msrpctypes.unmarshall_int32_ptr(rep_blob)
  --[out, switch_is(*pdwTypeId)] DNSSRV_RPC_UNION* ppData) -- pointer_default(unique)
  ptr, result['data'], pos = string.unpack("<I4B", rep_blob, pos)
  return result
end
function get_pad(data, align, pad_byte)
  pad_byte = pad_byte or "\00"
  return string.rep(pad_byte, (align-#data%align)%align)
end
return _ENV;
