local asn1 = require "asn1"
local coroutine = require "coroutine"
local datetime = require "datetime"
local io = require "io"
local math = require "math"
local match = require "match"
local netbios = require "netbios"
local nmap = require "nmap"
local smbauth = require "smbauth"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local unicode = require "unicode"
local smb2 = require "smb2"
_ENV = stdnse.module("smb", stdnse.seeall)
command_codes = {}
command_names = {}
status_codes = {}
status_names = {}
filetype_codes = {}
filetype_names = {}
local TIMEOUT = 10000
function add_account(host, username, domain, password, password_hash, hash_type, is_admin)
  smbauth.add_account(host, username, domain, password, password_hash, hash_type, is_admin)
end
function get_account(host)
  return smbauth.get_account(host)
end
function get_overrides(username, domain, password, password_hash, hash_type, overrides)
  if(not(overrides)) then
    return {username=username, domain=domain, password=password, password_hash=password_hash, hash_type=hash_type}
  else
    overrides['username'] = username
    overrides['domain'] = domain
    overrides['password'] = password
    overrides['password_hash'] = password_hash
    overrides['hash_type'] = hash_type
  end
end
function get_overrides_anonymous(overrides)
  if(not(overrides)) then
    return {username='', domain='', password='', password_hash=nil, hash_type='none'}
  else
    overrides['username'] = ''
    overrides['domain'] = ''
    overrides['password'] = ''
    overrides['password_hash'] = ''
    overrides['hash_type'] = 'none'
  end
end
function get_status_name(status)
  if(status_names[status] == nil) then
    for i, v in pairs(status_names) do
      if(v == status) then
        return i
      end
    end
    return string.format("NT_STATUS_UNKNOWN (0x%08x)", status)
  else
    return status_names[status]
  end
end
function get_port(host)
  local port_u137 = nmap.get_port_state(host, {number=137, protocol="udp"})
  local port_t139 = nmap.get_port_state(host, {number=139, protocol="tcp"})
  local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"})
  local custom_port = nil
  if(nmap.registry.args.smbport ~= nil) then
    custom_port = nmap.get_port_state(host, {number=tonumber(nmap.registry.args.smbport), protocol="tcp"})
  end
  if(custom_port ~= nil and custom_port.state == "open") then
    return custom_port.number
  end
  if(port_t445 ~= nil and port_t445.state == "open") then
    return 445
  end
  if(port_t139 ~= nil and port_t139.state == "open") then
    if(port_u137 == nil or port_u137.state == "open" or port_u137.state == "open|filtered") then
      return 139
    end
  end
  return nil
end
function disable_extended(smb)
  smb['extended_security'] = false
end
function start(host)
  local port = get_port(host)
  local status, result
  local state = {}
  state['uid']      = 0
  state['tid']      = 0
  state['mid']      = 1
  state['pid']      = math.random(32766) + 1
  state['host']     = host
  state['ip']       = host.ip
  state['sequence'] = -1
  if(stdnse.get_script_args( "smbbasic" )) then
    state['extended_security'] = false
  else
    state['extended_security'] = true
  end
  local nbcache_mutex = nmap.mutex("Netbios lookup mutex")
  nbcache_mutex "lock"
  if ( not(host.registry['netbios_name']) ) then
    status, result = netbios.get_server_name(host.ip)
    if(status == true) then
      host.registry['netbios_name'] = result
      state['name'] = result
    end
  else
    stdnse.debug2("SMB: Resolved netbios name from cache")
    state['name'] = host.registry['netbios_name']
  end
  nbcache_mutex "done"
  stdnse.debug2("SMB: Starting SMB session for %s (%s)", host.name, host.ip)
  if(port == nil) then
    return false, "SMB: Couldn't find a valid port to check"
  end
  smbauth.init_account(host)
  if(port ~= 139) then
    status, state['socket'] = start_raw(host, port)
    state['port'] = port
    if(status == false) then
      return false, state['socket']
    end
    return true, state
  else
    status, state['socket'] = start_netbios(host, port)
    state['port'] = port
    if(status == false) then
      return false, state['socket']
    end
    return true, state
  end
  return false, "SMB: Couldn't find a valid port to check"
end
function start_ex(host, bool_negotiate_protocol, bool_start_session, str_tree_connect, str_create_file, bool_disable_extended, overrides)
  local smbstate
  local status, err
  overrides = overrides or {}
  status, smbstate = start(host)
  if(status == false) then
    return false, smbstate
  end
  if(bool_disable_extended == true) then
    disable_extended(smbstate)
  end
  if(bool_negotiate_protocol == true) then
    status, err = negotiate_protocol(smbstate, overrides)
    if(status == false) then
      stop(smbstate)
      return false, err
    end
    if(bool_start_session == true) then
      status, err = start_session(smbstate, overrides)
      if(status == false) then
        stop(smbstate)
        return false, err
      end
      if(str_tree_connect ~= nil) then
        status, err = tree_connect(smbstate, str_tree_connect, overrides)
        if(status == false) then
          stop(smbstate)
          return false, err
        end
        if(str_create_file ~= nil) then
          status, err = create_file(smbstate, str_create_file, overrides)
          if(status == false) then
            stop(smbstate)
            return false, err
          end
        end
      end
    end
  end
  return true, smbstate
end
function stop(smb)
  if(smb['tid'] ~= 0) then
    tree_disconnect(smb)
  end
  if(smb['uid'] ~= 0) then
    logoff(smb)
  end
  stdnse.debug2("SMB: Closing socket")
  if(smb['socket'] ~= nil) then
    local status, err = smb['socket']:close()
    if(status == false) then
      return false, "SMB: Failed to close socket: " .. err
    end
  end
  return true
end
function start_raw(host, port)
  local status, err
  local socket = nmap.new_socket()
  socket:set_timeout(TIMEOUT)
  status, err = socket:connect(host, port, "tcp")
  if(status == false) then
    return false, "SMB: Failed to connect to host: " .. err
  end
  return true, socket
end
local function get_subnames(name)
  local i = -1
  local list = {}
  repeat
    local subname = name
    i = string.find(name, "[.]", i + 1)
    if(i ~= nil) then
      subname = string.sub(name, 1, i - 1)
    end
    list[#list + 1] = string.upper(subname)
  until i == nil
  return list
end
function start_netbios(host, port, name)
  local i
  local status, err
  local pos, result, flags, length
  local socket = nmap.new_socket()
  local names = {}
  if(name ~= nil) then
    names[#names + 1] = name
  end
  status, name = netbios.get_server_name(host.ip)
  if(status == true) then
    names[#names + 1] = name
  end
  names[#names + 1] = "*SMBSERVER"
  if(host.name ~= nil and host.name ~= "") then
    local new_names = get_subnames(host.name)
    for i = 1, #new_names, 1 do
      names[#names + 1] = new_names[i]
    end
  end
  i = 1
  repeat
    name = names[i]
    stdnse.debug1("SMB: Trying to start NetBIOS session with name = '%s'", name)
    local session_request = string.pack(">BBI2zz",
      0x81,                        -- session request
      0x00,                        -- flags
      0x44,                        -- length
      netbios.name_encode(name),   -- server name
      netbios.name_encode("NMAP")  -- client name
      );
    stdnse.debug3("SMB: Connecting to %s", host.ip)
    socket:set_timeout(TIMEOUT)
    status, err = socket:connect(host, port, "tcp")
    if(status == false) then
      socket:close()
      return false, "SMB: Failed to connect: " .. err
    end
    stdnse.debug3("SMB: Sending NetBIOS session request with name %s", name)
    status, err = socket:send(session_request)
    if(status == false) then
      socket:close()
      return false, "SMB: Failed to send: " .. err
    end
    socket:set_timeout(TIMEOUT)
    stdnse.debug3("SMB: Receiving NetBIOS session response")
    status, result = socket:receive_buf(match.numbytes(4), true);
    if(status == false) then
      socket:close()
      return false, "SMB: Failed to close socket: " .. result
    end
    result, flags, length, pos = string.unpack(">BBI2", result)
    if result == 0x82 then
      stdnse.debug3("SMB: Successfully established NetBIOS session with server name %s", name)
      return true, socket
    end
    stdnse.debug1("SMB: Session request failed, trying next name")
    socket:close()
    i = i + 1
  until i > #names
  stdnse.debug1("SMB: None of the NetBIOS names worked!")
  return false, "SMB: Couldn't find a NetBIOS name that works for the server. Sorry!"
end
function smb_encode_header(smb, command, overrides)
  overrides = overrides or {}
  local sig = "\xFFSMB"
  local flags  = (0x10 | 0x08) -- SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES
  local flags2 = (0x4000 | 0x2000 | 0x0040 | 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_EXECUTE_ONLY_READS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES
  if(nmap.registry.args.smbsign ~= "disable") then
    flags2 = (flags2 | 0x0004) -- SMB_FLAGS2_SECURITY_SIGNATURE
  end
  if(smb['extended_security'] == true) then
    flags2 = (flags2 | 0x0800) -- SMB_EXTENDED_SECURITY
  end
  if(smb['tid'] == nil) then
    return false, string.format("SMB: ERROR: TreeID value was set to nil on host %s", smb['ip'])
  end
  local header = string.pack("<BBBBB I4 B I2 I2 I8 I2 I2 I2 I2 I2",
    sig:byte(1),  -- Header
    sig:byte(2),  -- Header
    sig:byte(3),  -- Header
    sig:byte(4),  -- Header
    command,      -- Command
    (overrides['status'] or 0),        -- status
    (overrides['flags'] or flags),     -- flags
    (overrides['flags2'] or flags2),   -- flags2
    (overrides['pid_high'] or 0),      -- extra (pid_high)
    (overrides['signature'] or 0),     -- extra (signature)
    (overrides['extra'] or 0),         -- extra (unused)
    (overrides['tid'] or smb['tid']),  -- tid
    (overrides['pid'] or smb['pid']),  -- pid
    (overrides['uid'] or smb['uid']),  -- uid
    (overrides['mid'] or smb['mid'])   -- mid
    )
  return header
end
local function smb_encode_parameters(parameters, overrides)
  overrides = overrides or {}
  return string.pack("<B", overrides['parameters_length'] or (#parameters / 2)) .. parameters
end
local function smb_encode_data(data, overrides)
  overrides = overrides or {}
  return string.pack("<I2", overrides['data_length'] or #data) .. data
end
local function message_sign(smb, body)
  smb['sequence'] = smb['sequence'] + 1
  if(smb['mac_key'] == nil) then
    stdnse.debug3("SMB: Not signing message (missing mac_key)")
    return body
  elseif(nmap.registry.args.smbsign == "disable") then
    stdnse.debug3("SMB: Not signing message (disabled by user)")
    return body
  end
  local sequence = string.pack("<I8", smb['sequence'])
  local new_packet = string.sub(body, 1, 14) .. sequence .. string.sub(body, 23)
  local signature = smbauth.calculate_signature(smb['mac_key'], new_packet)
  return string.sub(body, 1, 14) .. signature .. string.sub(body, 23)
end
local function message_check_signature(smb, body)
  smb['sequence'] = smb['sequence'] + 1
  if(smb['mac_key'] == nil) then
    stdnse.debug3("SMB: Not signing message (missing mac_key)")
    return true
  elseif(nmap.registry.args.smbsign ~= "force" and (smb['security_mode'] & 0x0A) ~= 0) then
    stdnse.debug3("SMB: Not signing message (server doesn't support it -- default)")
    return true
  elseif(nmap.registry.args.smbsign == "disable" or nmap.registry.args.smbsign == "ignore") then
    stdnse.debug3("SMB: Not signing message (disabled by user)")
    return true
  end
  local signature  = string.sub(body, 15, 22)
  local sequence   = string.pack("<I8", smb['sequence'])
  local new_packet = string.sub(body, 1, 14) .. sequence .. string.sub(body, 23)
  local real_signature = smbauth.calculate_signature(smb['mac_key'], new_packet)
  return signature == real_signature
end
function smb_send(smb, header, parameters, data, overrides)
  overrides = overrides or {}
  local encoded_parameters = smb_encode_parameters(parameters, overrides)
  local encoded_data       = smb_encode_data(data, overrides)
  local body               = header .. encoded_parameters .. encoded_data
  local status, err
  body = message_sign(smb, body)
  local out = string.pack(">s4", body)
  stdnse.debug3("SMB: Sending SMB packet (len: %d)", #out)
  status, err = smb['socket']:send(out)
  if not status then
    stdnse.debug1("SMB: Sending packet failed.")
  end
  return status, err
end
function smb_read(smb, read_data)
  local pos, netbios_data, netbios_length, length, header, parameter_length, parameters, data_length, data
  stdnse.debug3("SMB: Receiving SMB packet")
  smb['socket']:set_timeout(TIMEOUT)
  local status, netbios_data = smb['socket']:receive_buf(match.numbytes(4), true);
  if not status then
    return false, "SMB: Failed to receive bytes: " .. netbios_data
  end
  netbios_length, pos = string.unpack(">I4", netbios_data)
  netbios_length = (netbios_length & 0x00FFFFFF)
  length = netbios_length + 4
  local status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true)
  if not status then
    return false, "SMB: Failed to receive bytes: " .. smb_data
  end
  local result = netbios_data .. smb_data
  if(#result ~= length) then
    stdnse.debug1("SMB: ERROR: Received wrong number of bytes, there will likely be issues (received %d, expected %d)", #result, length)
    return false, string.format("SMB: ERROR: Didn't receive the expected number of bytes; received %d, expected %d. This will almost certainly cause some errors.", #result, length)
  end
  local good_signature = message_check_signature(smb, string.sub(result, 5))
  if(good_signature == false) then
    return false, "SMB: ERROR: Server returned invalid signature"
  end
  local header_format = "<c32 B"
  if (#result - pos + 1) < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than needed for header"
  end
  header, parameter_length, pos = string.unpack("<c32 B", result, pos)
  if (length - pos + 1) < (parameter_length * 2) then
    return false, "SMB: ERROR: parameter_length greater than response length"
  end
  parameters, pos = string.unpack(("<c%d"):format(parameter_length*2), result, pos)
  data_length, pos = string.unpack("<I2", result, pos)
  if(read_data == nil or read_data == true) then
    if (length - pos + 1) < data_length then
      return false, "SMB: ERROR: data_length greater than response length"
    end
    data = string.unpack("c" .. data_length, result, pos)
  else
    data = nil
  end
  stdnse.debug3("SMB: Received %d bytes", #result)
  return true, header, parameters, data
end
function negotiate_v1(smb, overrides)
  local header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides)
  overrides = overrides or {}
  local parameters = ""
  local data = string.pack("<BzBz", 2, (overrides['dialect'] or "NT LM 0.12"), 2, "")
  stdnse.debug2("SMB: Sending SMB_COM_NEGOTIATE")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  local status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(protocol_version == ("\xFESMB")) then
    return false, "SMB: Server returned a SMBv2 packet, don't know how to handle"
  end
  if((flags2 & 0x0800) ~= 0x0800) then
    smb['extended_security'] = false
  end
  local dialect_format = "<I2"
  local parameters_format = "<BI2 I2 I4 I4 I4 I4"
  if #parameters < (string.packsize(dialect_format) + string.packsize(parameters_format)) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [9]"
  end
  smb['dialect'], pos = string.unpack(dialect_format, parameters)
  if(smb['dialect'] ~= 0) then
    stdnse.debug2("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
    return false, string.format("Server negotiated an unknown protocol (#%d) -- aborting", smb['dialect'])
  end
  smb.security_mode, smb.max_mpx, smb.max_vc, smb.max_buffer, smb.max_raw_buffer, smb.session_key, smb.capabilities, pos = string.unpack(parameters_format, parameters, pos)
  smb.time = 0
  smb.timezone = 0
  smb.key_length = 0
  smb.byte_count = 0
  if (#parameters - pos + 1) >= 8 then
    smb.time, pos = string.unpack("<I8", parameters, pos)
    if (#parameters - pos + 1) >= 2 then
      smb.timezone, pos = string.unpack("<i2", parameters, pos)
      if (#parameters - pos + 1) >= 1 then
        smb.key_length, pos = string.unpack("B", parameters, pos)
        if (#parameters - pos + 1) >= 2 then
          smb.byte_count, pos = string.unpack("<I2", parameters, pos)
        end
      end
    end
  end
  smb['time'] = (smb['time'] // 10000000) - 11644473600
  smb['date'] = datetime.format_timestamp(smb['time'])
  smb['timezone'] = -(smb['timezone'] / 60)
  if(smb['timezone'] == 0) then
    smb['timezone_str'] = "UTC+0"
  elseif(smb['timezone'] < 0) then
    smb['timezone_str'] = "UTC-" .. math.abs(smb['timezone'])
  else
    smb['timezone_str'] = "UTC+" .. smb['timezone']
  end
  if #data < smb.key_length then
    return false, "SMB: ERROR: not enough data for server_challenge"
  end
  smb.server_challenge, pos = string.unpack(string.format("<c%d", smb['key_length']), data)
  if(smb['extended_security'] == true) then
    if #data < 16 then
      return false, "SMB: ERROR: not enough data for extended security"
    end
    smb.server_guid, pos = string.unpack("<c16", data, pos)
    if ( #data - pos > 0 ) then
      smb.security_blob = data:sub(pos)
      pos = #data + 1
    end
  else
    smb['domain'] = ""
    smb['server'] = ""
    local remainder = unicode.utf16to8(string.sub(data, pos))
    pos, pos = string.find(remainder, "\0", 1, true)
    if pos == nil then
      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [14]"
    end
    smb['domain'] = string.sub(remainder, 1, pos)
    local pos2 = pos + 1
    pos, pos = string.find(remainder, "\0", pos2, true)
    if pos ~= nil then
      smb['server'] = string.sub(remainder, pos2, pos)
    end
  end
  stdnse.debug2("SMB_COM_NEGOTIATE got status:%s", status)
  if status == 0 then
    return true, overrides['dialect'] or "NT LM 0.12"
  end
end
function negotiate_protocol(smb, overrides)
  local status, dialect
  status, dialect = negotiate_v1(smb, overrides)
  if status then
    return true
  else
    stdnse.debug1("Couldn't negotiate a SMBv1 connection:%s", dialect)
    return false, string.format("Could not negotiate a connection:%s", dialect)
  end
end
function list_dialects(host, overrides)
  local smb2_dialects = {0x0202, 0x0210, 0x0300, 0x0302, 0x0311}
  local supported_dialects = {}
  local status, smb1_dialects
  local smbstate
  stdnse.debug2("Checking if SMBv1 is supported")
  status, smbstate = start(host)
  if(status == false) then
    return false, smbstate
  end
  status, smb1_dialects = negotiate_v1(smbstate, overrides)
  if status then --Add SMBv1 as a dialect
    table.insert(supported_dialects, smb1_dialects)
  end
  stop(smbstate)
  status = false -- Finish SMBv1 and close connection
  for i, dialect in pairs(smb2_dialects) do
    local dialect_human = stdnse.tohex(dialect, {separator = ".", group = 2})
    status, smbstate = start(host)
    if(status == false) then
      return false, smbstate
    end
    stdnse.debug2("Checking if dialect '%s' is supported", dialect_human)
    overrides['Dialects'] = {dialect}
    status, dialect = smb2.negotiate_v2(smbstate, overrides)
    if status then
      stdnse.debug2("SMB2: Dialect '%s' is supported", dialect_human)
      table.insert(supported_dialects, dialect_human)
    end
    stop(smbstate)
    status = false
  end
  return true, supported_dialects
end
local function start_session_basic(smb, log_errors, overrides)
  local i, err
  local status, result
  local header, parameters, data, domain
  local andx_command, andx_reserved, andx_offset, action
  local os, lanmanager
  local username, domain, password, password_hash, hash_type
  local busy_count = 0
  header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides)
  if(overrides ~= nil and overrides['username'] ~= nil) then
    result = true
    username      = overrides['username']
    domain        = overrides['domain']
    password      = overrides['password']
    password_hash = overrides['password_hash']
    hash_type     = overrides['hash_type']
  else
    result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
  end
  while result ~= false do
    local lanman, ntlm
    lanman, ntlm, smb['mac_key'] = smbauth.get_password_response(smb['ip'], username, domain, password, password_hash, hash_type, smb['server_challenge'], false)
    parameters = string.pack("<BBI2 I2I2 I2 I4 I2I2 I4I4",
      0xFF,               -- ANDX -- no further commands
      0x00,               -- ANDX -- Reserved (0)
      0x0000,             -- ANDX -- next offset
      0xFFFF,             -- Max buffer size
      0x0001,             -- Max multiplexes
      0x0001,             -- Virtual circuit num
      smb['session_key'], -- The session key
      #lanman,            -- ANSI/Lanman password length
      #ntlm,              -- Unicode/NTLM password length
      0x00000000,         -- Reserved
      0x00000050          -- Capabilities
      )
    data = lanman -- ANSI/Lanman password
    .. ntlm -- Unicode/NTLM password
    .. string.pack("<zzzz",
      username,               -- Account
      domain,                 -- Domain
      "Nmap",                 -- OS
      "Native Lanman"         -- Native LAN Manager
      )
    stdnse.debug2("SMB: Sending SMB_COM_SESSION_SETUP_ANDX")
    result, err = smb_send(smb, header, parameters, data, overrides)
    if(result == false) then
      return false, err
    end
    status, header, parameters, data = smb_read(smb)
    if(status ~= true) then
      return false, header
    end
    local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
    if #header < string.packsize(header_format) then
      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [17]"
    end
    local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
    if(status == 0) then
      local parameters_format = "<BB I2 I2"
      if #parameters < string.packsize(parameters_format) then
        return false, "SMB: ERROR: Server returned less data than needed"
      end
      andx_command, andx_reserved, andx_offset, action, pos = string.unpack(parameters_format, parameters)
      status, os, lanmanager, domain, pos = pcall(string.unpack, "<zzz", data)
      if not status then
        return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [19]"
      end
      smb['uid']        = uid
      smb['is_guest']   = (action & 1)
      smb['os']         = os
      smb['lanmanager'] = lanmanager
      if(os == "" or lanmanager == "" or domain == "") then
        stdnse.debug1("SMB: WARNING: the server is using a non-standard SMB implementation; your mileage may vary (%s)", smb['ip'])
      elseif(os == "Unix" or string.sub(lanmanager, 1, 5) == "Samba") then
        stdnse.debug1("SMB: WARNING: the server appears to be Unix; your mileage may vary.")
      end
      if(log_errors == nil or log_errors == true) then
        if(smb['is_guest'] == 1) then
          stdnse.debug1("SMB: Login as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", domain, stdnse.string_or_blank(username))
        else
          stdnse.debug2("SMB: Login as %s\\%s succeeded", domain, stdnse.string_or_blank(username))
        end
      end
      smb['sequence'] = 1
      return true
    else
      if(status == 0xc00000d0) then
        busy_count = busy_count + 1
        if(busy_count > 9) then
          return false, "SMB: ERROR: Server has too many active connections; giving up."
        end
        local backoff = math.random() * 10
        stdnse.debug1("SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100)
        stdnse.sleep(backoff)
      else
        if(log_errors == nil or log_errors == true) then
          stdnse.debug1("SMB: Login as %s\\%s failed (%s)", domain, stdnse.string_or_blank(username), get_status_name(status))
        end
        if(overrides == nil or overrides['username'] == nil) then
          smbauth.next_account(smb['host'])
          result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
        else
          result = false
        end
      end
    end
  end
  if(log_errors ~= false) then
    stdnse.debug1("SMB: ERROR: %s", username)
  end
  if (status ~= nil) then
    return false, get_status_name(status)
  else
    return false, username
  end
end
local function start_session_extended(smb, log_errors, overrides)
  local i
  local status, status_name, result, err
  local header, parameters, data
  local andx_command, andx_reserved, andx_offset, action, security_blob_length
  local os, lanmanager
  local username, domain, password, password_hash, hash_type
  local busy_count = 0
  status_name = "An unknown error has occurred"
  if(overrides ~= nil and overrides['username'] ~= nil) then
    result = true
    username      = overrides['username']
    domain        = overrides['domain']
    password      = overrides['password']
    password_hash = overrides['password_hash']
    hash_type     = overrides['hash_type']
  else
    result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
    if(not(result)) then
      return result, username
    end
  end
  local sp_nego = false
  if ( smb['security_blob'] and #smb['security_blob'] > 11 ) then
    local oid, pos = string.unpack(">c6", smb['security_blob'], 5)
    sp_nego = ( oid == "\x2b\x06\x01\x05\x05\x02" or oid == "\x06\x06\x2b\x06\x01\x05" ) -- check for SPNEGO OID 1.3.6.1.5.5.2
  end
  local ntlm_challenge_accepted = false
  while result ~= false do
    local security_blob = nil
    local security_blob_length = 0
    repeat
      if ( not(security_blob) ) then
        status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215))
        if ( sp_nego ) then
          local enc = asn1.ASN1Encoder:new()
          local mechtype = enc:encode( { type = 'A0', value = enc:encode( { type = '30', value = enc:encode( { type = '06', value = stdnse.fromhex("2b06010401823702020a") } ) } ) } )
          local oid = enc:encode( { type = '06', value = stdnse.fromhex("2b0601050502") } )
          security_blob = enc:encode(security_blob)
          security_blob = enc:encode( { type = 'A2', value = security_blob } )
          security_blob = mechtype .. security_blob
          security_blob = enc:encode( { type = '30', value = security_blob } )
          security_blob = enc:encode( { type = 'A0', value = security_blob } )
          security_blob = oid .. security_blob
          security_blob = enc:encode( { type = '60', value = security_blob } )
        end
      else
        if ( sp_nego ) then
          if (smb['domain'] or smb['server']) and (not domain or #domain == 0) then
            domain = smb['domain'] or smb['server']
          end
          hash_type = "ntlm"
        end
        status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215))
        if ( sp_nego ) then
          local enc = asn1.ASN1Encoder:new()
          security_blob = enc:encode(security_blob)
          security_blob = enc:encode( { type = 'A2', value = security_blob } )
          security_blob = enc:encode( { type = '30', value = security_blob } )
          security_blob = enc:encode( { type = 'A1', value = security_blob } )
        end
      end
      if(status == false) then
        return false, string.format("SMB: ERROR: Security blob: %s", security_blob)
      end
      header     = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides)
      data = security_blob -- Security blob
      .. string.pack("<zzz",
        "Nmap",                -- OS
        "Native Lanman",       -- Native LAN Manager
        ""                     -- Primary domain
        )
      parameters = string.pack("<BB I2 I2 I2 I2 I4 I2 I4 I4",
        0xFF,               -- ANDX -- no further commands
        0x00,               -- ANDX -- Reserved (0)
        #data + 24 + #header + 3, -- ANDX -- next offset
        0xFFFF,             -- Max buffer size
        0x0001,             -- Max multiplexes
        0x0001,             -- Virtual circuit num
        smb['session_key'], -- The session key
        #security_blob,     -- Security blob length
        0x00000000,         -- Reserved
        0x80000050          -- Capabilities
        )
      stdnse.debug2("SMB: Sending SMB_COM_SESSION_SETUP_ANDX")
      result, err = smb_send(smb, header, parameters, data, overrides)
      if(result == false) then
        return false, err
      end
      status, header, parameters, data = smb_read(smb)
      if(status ~= true) then
        return false, header
      end
      local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
      if #header < string.packsize(header_format) then
        return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
      end
      local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
      smb['uid'] = uid
      status_name = get_status_name(status)
      if(status_name == "NT_STATUS_SUCCESS" or status_name == "NT_STATUS_MORE_PROCESSING_REQUIRED") then
        local parameters_format = "<BBI2 I2 I2"
        if #parameters < string.packsize(parameters_format) then
          return false, "SMB: ERROR: Server returned less data than needed"
        end
        andx_command, andx_reserved, andx_offset, action, security_blob_length, pos = string.unpack(parameters_format, parameters)
        smb['is_guest']   = (action & 1)
        if #data < security_blob_length then
          return false, "SMB: ERROR: Server returned less data than needed"
        end
        security_blob, pos = string.unpack(("<c%d"):format(security_blob_length), data)
        status, os, lanmanager, pos = pcall(string.unpack, "zz", data, pos)
        if not ntlm_challenge_accepted then
          if ( status_name == "NT_STATUS_MORE_PROCESSING_REQUIRED" and sp_nego ) then
            local start = security_blob:find("NTLMSSP")
            security_blob = security_blob:sub(start)
          end
          if not status or security_blob == nil then
            return false, "SMB: ERROR: NTLM challenge not accepted or lanmanager missing"
          end
          smb['os']         = os
          smb['lanmanager'] = lanmanager
          local host_info = smbauth.get_host_info_from_security_blob(security_blob)
          if ( host_info ) then
            smb['fqdn'] = host_info['fqdn']
            smb['domain_dns'] = host_info['dns_domain_name']
            smb['forest_dns'] = host_info['dns_forest_name']
            smb['server'] = host_info['netbios_computer_name']
            smb['domain'] = host_info['netbios_domain_name']
          end
          ntlm_challenge_accepted = true
        end
        if(status_name == "NT_STATUS_SUCCESS") then
          if not status then
            stdnse.debug1("SMB: WARNING: the server is using a non-standard SMB implementation; your mileage may vary (%s)", smb['ip'])
          elseif(os == "Unix" or string.sub(lanmanager, 1, 5) == "Samba") then
            stdnse.debug1("SMB: WARNING: the server appears to be Unix; your mileage may vary.")
          end
          if(log_errors == nil or log_errors == true) then
            if(smb['is_guest'] == 1) then
              stdnse.debug1("SMB: Extended login to %s as %s\\%s failed, but was given guest access (username may be wrong, or system may only allow guest)", smb['ip'], domain, stdnse.string_or_blank(username))
            else
              stdnse.debug2("SMB: Extended login to %s as %s\\%s succeeded", smb['ip'], domain, stdnse.string_or_blank(username))
            end
          end
          smb['sequence'] = 1
          return true
        end -- Status is ok
      end -- Should we parse the parameters/data?
    until status_name ~= "NT_STATUS_MORE_PROCESSING_REQUIRED"
    if(status == 0xc00000d0) then
      busy_count = busy_count + 1
      if(busy_count > 9) then
        return false, "SMB: ERROR: Server has too many active connections; giving up."
      end
      local backoff = math.random() * 10
      stdnse.debug1("SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100)
      stdnse.sleep(backoff)
    else
      if(log_errors == nil or log_errors == true) then
        stdnse.debug1("SMB: Extended login to %s as %s\\%s failed (%s)", smb['ip'], domain, stdnse.string_or_blank(username), status_name)
      end
      if(overrides == nil or overrides['username'] == nil) then
        smbauth.next_account(smb['host'])
        result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host'])
        if(not(result)) then
          return false, username
        end
      else
        result = false
      end
    end
    smb['uid'] = 0
  end -- Loop over the accounts
  if(log_errors == nil or log_errors == true) then
    stdnse.debug1("SMB: ERROR: All logins failed, sorry it didn't work out!")
  end
  return false, status_name
end
function start_session(smb, overrides, log_errors)
  local smb_auth_mutex = nmap.mutex( "SMB Authentication Mutex" )
  smb_auth_mutex( "lock" )
  local status, result
  if(smb['extended_security'] == true) then
    status, result = start_session_extended(smb, log_errors, overrides)
  else
    status, result = start_session_basic(smb, log_errors, overrides)
  end
  smb_auth_mutex( "done" )
  return status, result
end
function tree_connect(smb, path, overrides)
  local header, parameters, data, err, result
  local andx_command, andx_reserved, andx_offset, action
  local status
  overrides = overrides or {}
  header = smb_encode_header(smb, command_codes['SMB_COM_TREE_CONNECT_ANDX'], overrides)
  parameters = string.pack("<BBI2 I2 I2",
    0xFF,   -- ANDX no further commands
    0x00,   -- ANDX reserved
    0x0000, -- ANDX offset
    (overrides['tree_connect_flags'] or 0x0000), -- flags
    0x0000 -- password length (for share-level security)
    )
  data = string.pack("zz",
    path,   -- Path
    (overrides['tree_type'] or "?????") -- Type of tree ("?????" = any)
    )
  stdnse.debug2("SMB: Sending SMB_COM_TREE_CONNECT_ANDX")
  result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  if(tid == 0 or tonumber(tid) == 0) then
    return false, "SMB: ERROR: Server didn't establish a proper tree connection (likely an embedded system)"
  end
  smb['tid'] = tid
  return true
end
function tree_disconnect(smb, overrides)
  overrides = overrides or {}
  local header
  header = smb_encode_header(smb, command_codes['SMB_COM_TREE_DISCONNECT'], overrides)
  stdnse.debug2("SMB: Sending SMB_COM_TREE_DISCONNECT")
  local result, err = smb_send(smb, header, "", "", overrides)
  if(result == false) then
    return false, err
  end
  local status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  smb['tid'] = 0
  return true
end
function logoff(smb, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local status
  header = smb_encode_header(smb, command_codes['SMB_COM_LOGOFF_ANDX'], overrides)
  parameters = string.pack("<BB I2",
    0xFF,   -- ANDX no further commands
    0x00,   -- ANDX reserved
    0x0000  -- ANDX offset
    )
  stdnse.debug2("SMB: Sending SMB_COM_LOGOFF_ANDX")
  local result, err = smb_send(smb, header, parameters, "", overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  smb['uid']      = 0
  smb['sequence'] = -1
  smb['mac_key']  = nil
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [22]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status == 0xc0000022) then
    stdnse.debug1("SMB: ERROR: Access was denied in 'logoff', indicating a problem with your message signatures")
    return false, "SMB: ERROR: Access was denied in 'logoff', indicating a problem with your message signatures"
  end
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  return true
end
function create_file(smb, path, overrides)
  local header, parameters, data
  local andx_command, andx_reserved, andx_offset
  local oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory
  local error_count = 0
  local status, pos
  repeat
    local mutex = nmap.mutex(smb['host'])
    mutex "lock"
    overrides = overrides or {}
    header = smb_encode_header(smb, command_codes['SMB_COM_NT_CREATE_ANDX'], overrides)
    parameters = string.pack("<BBI2 B I2 I4 I4 I4 I8 I4 I4 I4 I4 I4 B",
      0xFF,   -- ANDX no further commands
      0x00,   -- ANDX reserved
      0x0000, -- ANDX offset
      0x00,   -- Reserved
      #path, -- Path length
      (overrides['file_create_flags']            or 0x00000016),         -- Create flags
      (overrides['file_create_root_fid']         or 0x00000000),         -- Root FID
      (overrides['file_create_access_mask']      or 0x02000000),         -- Access mask
      (overrides['file_create_allocation_size']  or 0x0000000000000000), -- Allocation size
      (overrides['file_create_attributes']       or 0x00000000),         -- File attributes
      (overrides['file_create_share_attributes'] or 0x00000007),         -- Share attributes
      (overrides['file_create_disposition']      or 0x00000000),         -- Disposition
      (overrides['file_create_options']          or 0x00000000),         -- Create options
      (overrides['file_create_impersonation']    or 0x00000002),         -- Impersonation
      (overrides['file_create_security_flags']   or 0x01)                -- Security flags
      )
    data = string.pack("z", path)
    stdnse.debug2("SMB: Sending SMB_COM_NT_CREATE_ANDX")
    local result, err = smb_send(smb, header, parameters, data, overrides)
    if(result == false) then
      mutex "done"
      return false, err
    end
    status, header, parameters, data = smb_read(smb, false)
    mutex "done"
    if(status ~= true) then
      return false, header
    end
    local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
    if #header < string.packsize(header_format) then
      return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [23]"
    end
    local protocol_version, command, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid
    protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
    if(status == 0xc00000ac) then
      error_count = error_count + 1
      if(error_count > 10) then
        return false, "SMB: ERROR: Server returned NT_STATUS_PIPE_NOT_AVAILABLE too many times; giving up."
      end
      stdnse.debug1("WARNING: Server refused connection with NT_STATUS_PIPE_NOT_AVAILABLE; trying again")
      stdnse.sleep(.2)
    end
  until (status ~= 0xc00000ac)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  local parameters_format = "<BBI2 BI2 I4 I8 I8 I8 I8 I4 I8 I8 I2 I2 B"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory, pos = string.unpack(parameters_format, parameters)
  smb['oplock_level']    = oplock_level
  smb['fid']             = fid
  smb['create_action']   = create_action
  smb['created']         = created
  smb['last_access']     = last_access
  smb['last_write']      = last_write
  smb['last_change']     = last_change
  smb['attributes']      = attributes
  smb['allocation_size'] = allocation_size
  smb['end_of_file']     = end_of_file
  smb['filetype']        = filetype
  smb['ipc_state']       = ipc_state
  smb['is_directory']    = is_directory
  return true
end
function read_file(smb, offset, count, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local andx_command, andx_reserved, andx_offset
  local remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3
  local response = {}
  local status
  header = smb_encode_header(smb, command_codes['SMB_COM_READ_ANDX'], overrides)
  parameters = string.pack("<BBI2 I2 I4 I2 I2 I4 I2 I4",
    0xFF,   -- ANDX no further commands
    0x00,   -- ANDX reserved
    0x0000, -- ANDX offset
    smb['fid'], -- FID
    offset,     -- Offset
    count,      -- Max count low
    count,      -- Min count
    0xFFFFFFFF, -- Reserved
    0,          -- Remaining
    0x00000000  -- High offset
    )
  data = ""
  stdnse.debug2("SMB: Sending SMB_COM_READ_ANDX")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [25]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0 and
      (status ~= status_codes.NT_STATUS_BUFFER_OVERFLOW and (smb['filetype'] == filetype_codes.FILE_TYPE_BYTE_MODE_PIPE or
      smb['filetype'] == filetype_codes.FILE_TYPE_MESSAGE_MODE_PIPE) ) ) then
    return false, get_status_name(status)
  end
  local parameters_format = "<BBI2 I2 I2 I2 I2 I2 I4 I2 I4"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  andx_command, andx_reserved, andx_offset, remaining, data_compaction_mode, reserved_1, data_length_low, data_offset, data_length_high, reserved_2, reserved_3, pos = string.unpack(parameters_format, parameters)
  response['remaining']   = remaining
  response['data_length'] = (data_length_low | (data_length_high << 16))
  response['status']      = status
  if(response['data_length'] == 0) then
    response['data'] = 0
  else
    local data_start = #header + 1 + #parameters + 2
    if(data_offset < data_start) then
      return false, "SMB: Start of data isn't in data section"
    end
    data_offset = data_offset - data_start
    if(data_offset + response['data_length'] > #data) then
      return false, "SMB: Data returned runs off the end of the packet"
    end
    response['data'] = string.sub(data, data_offset + 1, data_offset + response['data_length'])
  end
  return true, response
end
function write_file(smb, write_data, offset, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local andx_command, andx_reserved, andx_offset
  local response = {}
  local status
  header = smb_encode_header(smb, command_codes['SMB_COM_WRITE_ANDX'], overrides)
  parameters = string.pack("<BBI2 I2 I4 I4 I2 I2 I2 I2 I2 I4",
    0xFF,   -- ANDX no further commands
    0x00,   -- ANDX reserved
    0x0000, -- ANDX offset
    smb['fid'], -- FID
    offset,     -- Offset
    0xFFFFFFFF, -- Reserved
    0x0008,     -- Write mode (Message start, don't write raw, don't return remaining, don't write through
    #write_data,-- Remaining
    0x0000,     -- Data length high
    #write_data,-- Data length low -- TODO: set this properly (to the 2-byte value)
    0x003F,     -- Data offset
    0x00000000  -- Data offset high
    )
  data = write_data
  stdnse.debug2("SMB: Sending SMB_COM_WRITE_ANDX")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  local parameters_format = "<BBI2 I2 I2 I2 I2"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  local count_reserved, count_high, remaining, count_low
  andx_command, andx_reserved, andx_offset, count_low, remaining, count_high, count_reserved, pos = string.unpack(parameters_format, parameters)
  response['count_low']  = count_low
  response['remaining']  = remaining
  response['count_high'] = count_high
  response['reserved']   = count_reserved
  return true, response
end
function close_file(smb, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local pos
  local status
  local andx_command, andx_reserved, andx_offset
  local response = {}
  header = smb_encode_header(smb, command_codes['SMB_COM_CLOSE'], overrides)
  parameters = string.pack("<I2 I4",
    smb['fid'], -- FID
    0xFFFFFFFF  -- Last write (unspecified)
    )
  data = ""
  stdnse.debug2("SMB: Sending SMB_CLOSE")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [27]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  return true, response
end
function delete_file(smb, path, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local andx_command, andx_reserved, andx_offset
  local status
  header = smb_encode_header(smb, command_codes['SMB_COM_DELETE'], overrides)
  parameters = string.pack("<I2",
    0x0027 -- Search attributes (0x27 = include read only, hidden, system, and archive)
    )
  data = string.pack("<Bz",
    0x04, -- Ascii formatted filename
    path)
  stdnse.debug2("SMB: Sending SMB_CLOSE")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    return false, get_status_name(status)
  end
  return true
end
local function send_transaction2(smb, sub_command, function_parameters, function_data, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local parameter_offset = 0
  local parameter_size   = 0
  local data_offset      = 0
  local data_size        = 0
  local total_word_count, total_data_count, reserved1, parameter_count, parameter_displacement, data_count, data_displacement, setup_count, reserved2
  local response = {}
  header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION2'], overrides) -- 0x32 = SMB_COM_TRANSACTION2
  if(function_parameters) then
    parameter_offset = 0x44
    parameter_size = #function_parameters
    data_offset = #function_parameters + 33 + 32
  end
  parameters = string.pack("<I2 I2 I2 I2 BBI2 I4 I2 I2 I2 I2 I2 BBI2 ",
    parameter_size,                  -- Total parameter count.
    data_size,                       -- Total data count.
    0x000a,                          -- Max parameter count.
    0x3984,                          -- Max data count.
    0x00,                            -- Max setup count.
    0x00,                            -- Reserved.
    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
    0x00001388,                      -- Timeout (0x00000000 = return immediately).
    0x0000,                          -- Reserved.
    parameter_size,                  -- Parameter bytes.
    parameter_offset,                -- Parameter offset.
    data_size,                       -- Data bytes.
    data_offset,                     -- Data offset.
    0x01,                            -- Setup Count
    0x00,                            -- Reserved
    sub_command                      -- Sub command
    )
  local data = "\0\0\0" .. (function_parameters or '')
  .. (function_data or '')
  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION2")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  return true
end
local function receive_transaction2(smb)
  local status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    if(status_names[status] == nil) then
      return false, string.format("Unknown SMB error: 0x%08x\n", status)
    else
      return false, status_names[status]
    end
  end
  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  local total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
  parameter_offset = parameter_offset - 0x20 - 0x01 - #parameters - 0x02;
  data_offset = data_offset - 0x20 - 0x01 - #parameters - 0x02;
  local function_parameters = string.sub(data, parameter_offset + 1, parameter_offset + parameter_count)
  local function_data       = string.sub(data, data_offset      + 1, data_offset      + data_count)
  local response = {}
  response['parameters'] = function_parameters
  response['data']       = function_data
  return true, response
end
function send_transaction_named_pipe(smb, function_parameters, function_data, pipe, no_setup, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local parameter_offset = 0
  local parameter_size   = 0
  local data_offset      = 0
  local data_size        = 0
  local total_word_count, total_data_count, reserved1, parameter_count, parameter_displacement, data_count, data_displacement, setup_count, reserved2
  local response = {}
  local status
  if(pipe == nil) then
    pipe = "\\PIPE\\"
  end
  header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION'], overrides) -- 0x25 = SMB_COM_TRANSACTION
  if(function_parameters) then
    parameter_offset = 0x20 + 0x01 + 0x20 + 0x02 + (#pipe + 1)
    parameter_size = #function_parameters
  end
  if(function_data) then
    data_offset       = 0x20 + 0x01 + 0x20 + 0x02 + (#pipe + 1) + parameter_size
    data_size         = #function_data
  end
  local setup
  if(no_setup) then
    setup = string.pack("<BB",
      0x00,                            -- Number of 'setup' words (none)
      0x00                             -- Reserved.
      )
  else
    setup = string.pack("<BBI2 I2 ",
      0x02,                            -- Number of 'setup' words
      0x00,                            -- Reserved.
      0x0026,                          -- Function to call.
      smb['fid']                       -- Handle to open file
      )
  end
  parameters = string.pack("<I2 I2 I2 I2 BBI2 I4 I2 I2 I2 I2 I2",
    parameter_size,                  -- Total parameter count.
    data_size,                       -- Total data count.
    0x0008,                          -- Max parameter count.
    0x3984,                          -- Max data count.
    0x00,                            -- Max setup count.
    0x00,                            -- Reserved.
    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
    0x00001388,                      -- Timeout (0x00000000 = return immediately).
    0x0000,                          -- Reserved.
    parameter_size,                  -- Parameter bytes.
    parameter_offset,                -- Parameter offset.
    data_size,                       -- Data bytes.
    data_offset                      -- Data offset.
    ) .. setup
  data = string.pack("<zI4", pipe, 0) -- Padding
  .. (function_parameters or '')
  .. (function_data or '')
  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    if(status_names[status] == nil) then
      return false, string.format("Unknown SMB error: 0x%08x\n", status)
    else
      return false, status_names[status]
    end
  end
  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
  parameter_offset = parameter_offset - 0x20 - 0x01 - #parameters - 0x02;
  data_offset = data_offset - 0x20 - 0x01 - #parameters - 0x02;
  function_parameters = string.sub(data, parameter_offset + 1, parameter_offset + parameter_count)
  function_data       = string.sub(data, data_offset      + 1, data_offset      + data_count)
  response['parameters'] = function_parameters
  response['data']       = function_data
  return true, response
end
function send_transaction_waitnamedpipe(smb, priority, pipe, overrides)
  overrides = overrides or {}
  local header, parameters, data
  local parameter_offset, data_offset
  local total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2
  local response = {}
  local padding = ""
  local status
  header = smb_encode_header(smb, command_codes['SMB_COM_TRANSACTION'], overrides) -- 0x25 = SMB_COM_TRANSACTION
  parameters = string.pack("<I2 I2 I2 I2 BBI2 I4 I2 I2 I2 I2 I2 BBI2 I2 ",
    0,                               -- Total parameter count.
    0,                               -- Total data count.
    0x000,                           -- Max parameter count.
    0x400,                           -- Max data count.
    0x00,                            -- Max setup count.
    0x00,                            -- Reserved.
    0x0000,                          -- Flags (0x0000 = 2-way transaction, don't disconnect TIDs).
    30,                              -- Timeout (0x00000000 = return immediately).
    0x0000,                          -- Reserved.
    0,                               -- Parameter bytes.
    0,                               -- Parameter offset.
    0,                               -- Data bytes.
    0,                               -- Data offset.
    0x02,                            -- Number of 'setup' words (only ever seen '2').
    0x00,                            -- Reserved.
    0x0053,                          -- Function to call.
    priority                         -- Handle to open file
    )
  data = string.pack("z", pipe) .. string.rep('\0', (4 - ((#pipe+1) % 4)) % 4)
  stdnse.debug2("SMB: Sending SMB_COM_TRANSACTION (WaitNamedPipe)")
  local result, err = smb_send(smb, header, parameters, data, overrides)
  if(result == false) then
    return false, err
  end
  status, header, parameters, data = smb_read(smb)
  if(status ~= true) then
    return false, header
  end
  local header_format = "<c4 B I4 B I2 I2 i8 I2 I2 I2 I2 I2"
  if #header < string.packsize(header_format) then
    return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [8]"
  end
  local protocol_version, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid, pos = string.unpack(header_format, header)
  if(status ~= 0) then
    if(status_names[status] == nil) then
      return false, string.format("Unknown SMB error: 0x%08x\n", status)
    else
      return false, status_names[status]
    end
  end
  local parameters_format = "<I2 I2 I2 I2 I2 I2 I2 I2 I2 BB"
  if #parameters < string.packsize(parameters_format) then
    return false, "SMB: ERROR: Server returned less data than needed"
  end
  total_word_count, total_data_count, reserved1, parameter_count, parameter_offset, parameter_displacement, data_count, data_offset, data_displacement, setup_count, reserved2, pos = string.unpack(parameters_format, parameters)
  return true, response
end
function file_upload(host, localfile, share, remotefile, overrides, encoded)
  local status, err, smbstate
  local chunk = 1024
  local handle = io.open(localfile, "r")
  if(not(handle)) then
    stdnse.debug1("Couldn't open %s directly, searching Nmap's paths...", localfile)
    local filename = nmap.fetchfile(localfile)
    if(filename == nil) then
      return false, string.format("Couldn't find the file to upload (%s)", localfile)
    end
    handle = io.open(filename, "r")
  end
  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
  if(status == false) then
    return false, smbstate
  end
  local i = 0
  local data = handle:read(chunk)
  local new_data = {}
  while(data ~= nil and #data > 0) do
    if(encoded) then
      for j = 1, #data, 1 do
        new_data[j] = string.char(0xFF ~ string.byte(data, j))
      end
      data = table.concat(new_data, "", 1, #data)
    end
    status, err = write_file(smbstate, data, i)
    if(status == false) then
      stop(smbstate)
      return false, err
    end
    data = handle:read(chunk)
    i = i + chunk
  end
  handle:close()
  status, err = close_file(smbstate)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  stop(smbstate)
  return true
end
function file_write(host, data, share, remotefile, use_anonymous)
  local status, err, smbstate
  local chunk = 1024
  local overrides = nil
  if(use_anonymous) then
    overrides = get_overrides_anonymous()
  end
  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
  if(status == false) then
    return false, smbstate
  end
  local i = 1
  while(i <= #data) do
    local chunkdata = string.sub(data, i, i + chunk - 1)
    status, err = write_file(smbstate, chunkdata, i - 1)
    if(status == false) then
      stop(smbstate)
      return false, err
    end
    i = i + chunk
  end
  status, err = close_file(smbstate)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  stop(smbstate)
  return true
end
function file_read(host, share, remotefile, use_anonymous, overrides)
  local status, err, smbstate
  local result
  local chunk = 1024
  local read = ""
  overrides = overrides or {}
  if(use_anonymous) then
    overrides = get_overrides_anonymous(overrides)
  end
  status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides)
  if(status == false) then
    return false, smbstate
  end
  local i = 1
  while true do
    status, result = read_file(smbstate, i - 1, chunk)
    if(status == false) then
      stop(smbstate)
      return false, result
    end
    if(result['data_length'] == 0) then
      break
    end
    read = read .. result['data']
    i = i + chunk
  end
  status, err = close_file(smbstate)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  stop(smbstate)
  return true, read
end
function files_exist(host, share, files, overrides)
  local status, smbstate, result, err
  overrides = overrides or {}
  overrides['file_create_disposition'] = 1
  status, smbstate = start_ex(host, true, true, share, nil, nil, overrides)
  if(status == false) then
    return false, smbstate
  end
  local exist = 0
  local list  = {}
  for _, file in ipairs(files) do
    status, result = create_file(smbstate, file, overrides)
    if(not(status) and result ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then
      return false, result
    end
    if(status) then
      exist = exist + 1
      table.insert(list, file)
      status, err = close_file(smbstate)
      if(status == false) then
        stop(smbstate)
        return false, err
      end
    end
  end
  stop(smbstate)
  return true, exist, list
end
function file_delete(host, share, remotefile)
  local status, smbstate, err
  status, smbstate = start_ex(host, true, true, share)
  if(status == false) then
    return false, smbstate
  end
  if(type(remotefile) ~= "table") then
    remotefile = {remotefile}
  end
  for _, file in ipairs(remotefile) do
    status, err = delete_file(smbstate, file)
    if(status == false) then
      stdnse.debug1("SMB: Couldn't delete %s\\%s: %s", share, file, err)
      if(err ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then
        stop(smbstate)
        return false, err
      end
    end
  end
  stop(smbstate)
  return true
end
function find_files(smbstate, fname, options)
  local TRANS2_FIND_FIRST2, TRANS2_FIND_NEXT2 = 1, 2
  options = options or {}
  if (not(options.srch_attrs)) then
    options.srch_attrs = { ro = true, hidden = true, system = true, dir = true}
  end
  local nattrs = (( options.srch_attrs.ro and 1 or 0 ) + ( options.srch_attrs.hidden and 2 or 0 ) +
    ( options.srch_attrs.hidden and 2 or 0 ) + ( options.srch_attrs.system and 4 or 0 ) +
    ( options.srch_attrs.volid and 8 or 0 ) + ( options.srch_attrs.dir and 16 or 0 ) +
    ( options.srch_attrs.archive and 32 or 0 ))
  if ( not(fname) ) then
    fname = '\\*\0'
  elseif( fname:sub(1,1) ~= '\\' ) then
    fname = '\\' .. fname .. '\0'
  end
  local function send_and_receive_find_request(smbstate, trans_type, function_parameters)
    local status, err = send_transaction2(smbstate, trans_type, function_parameters, "")
    if ( not(status) ) then
      return false, "Failed to send data to server: send_transaction2"
    end
    local status, response = receive_transaction2(smbstate)
    if not status or #response.parameters < 2 then
      return false, "Failed to receive data from server: receive_transaction2"
    end
    local pos = ( TRANS2_FIND_FIRST2 == trans_type and 9 or 7 )
    local last_name_offset = string.unpack("<I2", response.parameters, pos)
    local NE_UP_TO_FNAME_SIZE = 94
    while ( last_name_offset > ( #response.data - NE_UP_TO_FNAME_SIZE ) ) do
      local status, tmp = receive_transaction2(smbstate)
      if ( not(status) ) then
        return false, "Failed to receive data from receive_transaction2"
      end
      response.data = response.data .. tmp.data
    end
    return true, response
  end
  local srch_count = 173 -- picked up by wireshark
  local flags = 6 -- Return RESUME keys, close search if END OF SEARCH is reached
  local loi   = 260 -- Level of interest, return SMB_FIND_FILE_BOTH_DIRECTORY_INFO
  local storage_type = 0 -- despite the documentation of having to be either 0x01 or 0x40, wireshark reports 0
  local pad = ( 32 + 36 + 12 + #fname ) % 4
  local function_parameters = string.pack("<I2 I2 I2 I2 I4",
    nattrs, srch_count, flags, loi, storage_type) .. fname .. string.rep('\0', (4 - pad) % 4)
  local function next_item()
    local status, response = send_and_receive_find_request(smbstate, TRANS2_FIND_FIRST2, function_parameters)
    if not status or #response.parameters < 4 then
      return
    end
    local srch_id = string.unpack("<I2", response.parameters)
    local stop_loop = ( string.unpack("<I2", response.parameters, 5) ~= 0 )
    local first = true
    local last_name
    repeat
      local pos = 1
      if ( not(first) ) then
        local function_parameters = string.pack("<I2 I2 I2 I4 I2 z", srch_id, srch_count, loi, 0, flags, last_name)
        status, response = send_and_receive_find_request(smbstate, TRANS2_FIND_NEXT2, function_parameters)
        if not status or #response.parameters < 2 then
          return
        end
        stop_loop = ( string.unpack(">I2", response.parameters, 3) ~= 0 )
      end
      local fe_format = "<I4 I4 I8 I8 I8 I8 I8 I8 I4 I4 I4 Bx c24"
      while #response.data - pos + 1 >= string.packsize(fe_format) do
        local fe = {}
        local last_pos = pos
        local ne, f_len, ea_len, sf_len
        ne, fe.fi, fe.created, fe.accessed, fe.write, fe.change,
        fe.eof, fe.alloc_size, fe.attrs, f_len, ea_len, sf_len, fe.s_fname, pos = string.unpack(fe_format, response.data, pos)
        local time = fe.created
        time = (time // 10000000) - 11644473600
        fe.created = datetime.format_timestamp(time)
        if #response.data - pos + 1 < f_len then
          break
        end
        fe.fname, pos = string.unpack("c" .. f_len, response.data, pos)
        pos = last_pos + ne
        fe.fname = fe.fname:sub(1, -2)
        last_name = fe.fname
        coroutine.yield(fe)
        if ne == 0 then
          break
        end
      end
      first = false
    until(stop_loop)
    return
  end
  return coroutine.wrap(next_item)
end
function share_anonymous_can_write(host, share)
  local filename, status, err
  filename = "nmap-test-file"
  status, err = file_write(host, string.rep("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10), share, filename, true)
  if(status == false) then
    if(err == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
      return false, err
    end
    if(err == "NT_STATUS_ACCESS_DENIED" or err == "NT_STATUS_INVALID_PARAMETER") then
      return true, false
    end
    return false, "Error writing test file to disk as anonymous: " .. err
  end
  status, err = file_delete(host, share, filename)
  if(status == false) then
    return false, "Error deleting test file as anonymous: " .. err
  end
  return true, true
end
function share_user_can_write(host, share)
  local filename, status, err
  filename = "nmap-test-file"
  status, err = file_write(host, string.rep("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 10), share, filename)
  if(status == false) then
    if(err == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
      return false, err
    end
    if(err == "NT_STATUS_ACCESS_DENIED" or err == "NT_STATUS_INVALID_PARAMETER") then
      return true, false
    end
    return false, "Error writing test file to disk as user: " .. err
  end
  status, err = file_delete(host, share, filename)
  if(status == false) then
    return false, "Error deleting test file as user: " .. err
  end
  return true, true
end
function share_anonymous_can_read(host, share)
  local status, smbstate, err
  local overrides = get_overrides_anonymous()
  status, smbstate = start(host)
  if(status == false) then
    return false, smbstate
  end
  status, err = negotiate_protocol(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  status, err = start_session(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  status, err = tree_connect(smbstate, share, overrides)
  if(status == false) then
    stop(smbstate)
    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
      return true, false
    else
      return false, err
    end
  end
  stop(smbstate)
  return true, true
end
function share_user_can_read(host, share)
  local status, smbstate, err
  local overrides = {}
  status, smbstate = start(host)
  if(status == false) then
    return false, smbstate
  end
  status, err = negotiate_protocol(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  status, err = start_session(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  status, err = tree_connect(smbstate, share, overrides)
  if(status == false) then
    stop(smbstate)
    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
      return true, false
    else
      return false, err
    end
  end
  stop(smbstate)
  return true, true
end
function share_host_returns_proper_error(host, use_anonymous)
  local status, smbstate, err
  local share = "nmap-share-test"
  local overrides
  if ( use_anonymous ) then
    overrides = get_overrides_anonymous()
  end
  status, smbstate = start(host)
  if(status == false) then
    return false, smbstate
  end
  status, err = negotiate_protocol(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  status, err = start_session(smbstate, overrides)
  if(status == false) then
    stop(smbstate)
    return false, err
  end
  stdnse.debug1("SMB: Trying a random share to see if server responds properly: %s", share)
  status, err = tree_connect(smbstate, share, overrides)
  if(status == false) then
    if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then
      stdnse.debug1("SMB: Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)")
      stop(smbstate)
      return true, false
    end
  else
    stdnse.debug1("SMB: Server doesn't return proper value for non-existent shares (accepts the connection)")
    stop(smbstate)
    return true, false
  end
  stop(smbstate)
  return true, true
end
function share_get_details(host, share)
  local msrpc = require "msrpc" -- avoid require cycle
  local smbstate, status, result
  local i
  local details = {}
  status, share = get_fqpn(host, share)
  if not status then
    stdnse.debug1("SMB:Couldn't obtain FQPN share name. Trying with '%s'", share)
  end
  details['name'] = share
  stdnse.debug1("SMB: Checking if share %s can be read by the current user", share)
  status, result = share_user_can_read(host, share)
  if(status == false) then
    return false, result
  end
  details['user_can_read'] = result
  stdnse.debug1("SMB: Checking if share %s can be read by the anonymous user", share)
  status, result = share_anonymous_can_read(host, share)
  if(status == true) then
    details['anonymous_can_read'] = result
  end
  stdnse.debug1("SMB: Checking if share %s can be written by the current user", share)
  status, result = share_user_can_write(host, share)
  if(status == false) then
    if(result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
      details['user_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND"
    else
      return false, result
    end
  end
  details['user_can_write'] = result
  stdnse.debug1("SMB: Checking if share %s can be written by the anonymous user", share)
  status, result = share_anonymous_can_write(host, share)
  if(status == false and result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
    details['anonymous_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND"
  elseif( status == true ) then
    details['anonymous_can_write'] = result
  end
  status, result = msrpc.get_share_info(host, share)
  if(status == false) then
    stdnse.debug1("SMB: Failed to get share info for %s: %s", share, result)
    details['details'] = result
  else
    result = result['info']
    if(result['max_users'] == 0xFFFFFFFF) then
      result['max_users'] = "<unlimited>"
    end
    details['details'] = result
  end
  return true, details
end
function share_get_list(host)
  local msrpc = require "msrpc" -- avoid require cycle
  local status, result
  local enum_status
  local extra = ""
  local shares = {}
  local share_details = {}
  stdnse.debug1("SMB: Attempting to log into the system to enumerate shares")
  enum_status, shares = msrpc.enum_shares(host)
  if(enum_status == false) then
    stdnse.debug1("SMB: Enumerating shares failed, guessing at common ones (%s)", shares)
    extra = string.format("ERROR: Enumerating shares failed, guessing at common ones (%s)", shares)
    shares = {"ADMIN", "BACKUP", "DATA", "DESKTOP", "DOCS", "FILES", "GROUPS", "HD", "HOME", "INFO", "IPC", "MEDIA", "MY DOCUMENTS", "NETLOGON", "PICTURES", "PORN", "PR0N", "PRINT", "PROGRAMS", "PRON", "PUBLIC", "SHARE", "SHARED", "SOFTWARE", "STMP", "TEMP", "TEST", "TMP", "USERS", "WEB DOCUMENTS","WEBSERVER", "WWW", "XSERVE" }
    for i = string.byte("A", 1), string.byte("Z", 1), 1 do
      shares[#shares + 1] = string.char(i)
    end
    local sharesLength = #shares
    for shareItr = 1, sharesLength, 1 do
      shares[ sharesLength + shareItr ] = shares[ shareItr ] .. '$'
    end
  else
    stdnse.debug1("SMB: Found %d shares, will attempt to find more information", #shares)
  end
  table.sort(shares)
  for _, anon in ipairs({true, false}) do
    status, result = share_host_returns_proper_error(host, anon)
    if(status == true and result == false) then
      return false, "Server doesn't return proper value for non-existent shares; can't enumerate shares"
    end
  end
  if(status == false) then
    return false, result
  end
  for i = 1, #shares, 1 do
    local status, result
    stdnse.debug1("SMB: Getting information for share: %s", shares[i])
    status, result = share_get_details(host, shares[i])
    if(status == false and result == 'NT_STATUS_BAD_NETWORK_NAME') then
      stdnse.debug1("SMB: Share doesn't exist: %s", shares[i])
    elseif(status == false) then
      stdnse.debug1("SMB: Error while getting share details: %s", result)
      return false, result
    else
      table.insert(share_details, result)
    end
  end
  return true, share_details, extra
end
function share_find_writable(host)
  local i
  local status, shares
  local main_name, main_path
  local names = {}
  local writable = {}
  status, shares = share_get_list(host)
  if(status == false) then
    return false, shares
  end
  for i = 1, #shares, 1 do
    if(shares[i]['user_can_write'] == true) then
      if(main_name == nil) then
        main_name = shares[i]['name']
        if(shares[i]['details'] ~= nil) then
          main_path = shares[i]['details']['path']
        end
      end
      table.insert(names, shares[i]['name'])
    end
  end
  if(main_name == nil) then
    return false, "Couldn't find a writable share!"
  else
    return true, main_name, main_path, names
  end
end
function get_windows_version(os)
  if(os == "Windows 5.0") then
    return "Windows 2000"
  elseif(os == "Windows 5.1")then
    return "Windows XP"
  end
  return os
end
function get_os(host)
  local state
  local status, smbstate
  local response = {}
  status, smbstate = start_ex(host, true, true, nil, nil, true)
  if(status == false) then
    return false, smbstate
  end
  if(smbstate['os'] == nil and smbstate['lanmanager'] == nil) then
    return false, "Server didn't return OS details"
  end
  response['os']           = smbstate['os']
  response['lanmanager']   = smbstate['lanmanager']
  response['domain']       = smbstate['domain']
  response['server']       = smbstate['server']
  response['date']         = smbstate['date']
  response['time']         = smbstate['time']
  response['timezone_str'] = smbstate['timezone_str']
  response['timezone']     = smbstate['timezone']
  response['port']         = smbstate['port']
  stop(smbstate)
  status, smbstate = start_ex(host, true, true, nil, nil, false)
  if(status == true) then
    if (smbstate['fqdn'] or smbstate['domain_dns'] or smbstate['forest_dns']) then
      response['fqdn']         = smbstate['fqdn']
      response['domain_dns']   = smbstate['domain_dns']
      response['forest_dns']   = smbstate['forest_dns']
      if ( smbstate['domain'] ~= nil and response['domain'] ~= smbstate['domain'] ) then
        response['workgroup']    = response['domain']
        response['domain']       = nil
      end
    end
    stop(smbstate)
  end
  return true, response
end
function get_socket_info(host)
  local status, lhost, lport, rhost, rport
  local smbstate, socket
  status, smbstate = start_ex(host)
  if(status == false) then
    return false, smbstate
  end
  socket = smbstate['socket']
  status, lhost, lport, rhost, rport = socket:get_info()
  if(status == false) then
    return false, lhost
  end
  stop(smbstate)
  local lmac = nil
  if(host.mac_addr_src) then
    lmac = stdnse.tohex(host.mac_addr_src, {separator = ":"})
  end
  return true, lhost, lport, rhost, rport, lmac
end
function get_uniqueish_name(host, extension, seed)
  local status
  local lhost, lport, rhost, rport
  if(type(host) == "table") then
    status, lhost = get_socket_info(host)
  else
    lhost = host
  end
  local hash = 0
  local i
  local str = lhost .. (seed or "") .. (extension or "") .. (nmap.registry.args.randomseed or "")
  for i = 1, #str, 1 do
    local chr = str:byte(i)
    hash = hash ~ chr
    hash = (hash << 3) | (hash >> 29)
    hash = hash ~ 3
    hash = hash & 0xFFFFFFFF
  end
  local response
  if(extension) then
    response = string.format("%x.%s", hash, extension)
  else
    response = string.format("%x", hash)
  end
  return true, response
end
function is_admin(host, username, domain, password, password_hash, hash_type)
  local msrpc = require "msrpc" -- avoid require cycle
  local overrides = get_overrides(username, domain, password, password_hash, hash_type)
  stdnse.debug1("SMB: Checking if %s is an administrator", username)
  local status, smbstate = start(host)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to start SMB: %s [%s]", smbstate, username)
    stop(smbstate)
    return false
  end
  local status, err      = negotiate_protocol(smbstate, overrides)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to negotiate protocol: %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  status, err      = start_session(smbstate, overrides)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to start session %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  local _, fqpn_share = get_fqpn(host, "IPC$")
  status, err      = tree_connect(smbstate, fqpn_share, overrides)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to connect tree: %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  status, err      = create_file(smbstate, msrpc.SRVSVC_PATH, overrides)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to create file: %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  status, err      = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Failed to bind: %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  status, err = msrpc.srvsvc_netservergetstatistics(smbstate, host.ip)
  if(status == false) then
    stdnse.debug1("SMB; is_admin: Couldn't get server stats (may be normal): %s [%s]", err, username)
    stop(smbstate)
    return false
  end
  stop(smbstate)
  return true
end
function get_fqpn(host, sharename)
  if host.ip and sharename then
    return true, string.format("\\\\%s\\%s", host.ip, sharename)
  end
  stdnse.debug1("SMB: get_fqpn: Couldn't determine server IP address")
  return false, sharename
end
command_codes =
{
  SMB_COM_CREATE_DIRECTORY          = 0x00,
  SMB_COM_DELETE_DIRECTORY          = 0x01,
  SMB_COM_OPEN                      = 0x02,
  SMB_COM_CREATE                    = 0x03,
  SMB_COM_CLOSE                     = 0x04,
  SMB_COM_FLUSH                     = 0x05,
  SMB_COM_DELETE                    = 0x06,
  SMB_COM_RENAME                    = 0x07,
  SMB_COM_QUERY_INFORMATION         = 0x08,
  SMB_COM_SET_INFORMATION           = 0x09,
  SMB_COM_READ                      = 0x0A,
  SMB_COM_WRITE                     = 0x0B,
  SMB_COM_LOCK_BYTE_RANGE           = 0x0C,
  SMB_COM_UNLOCK_BYTE_RANGE         = 0x0D,
  SMB_COM_CREATE_TEMPORARY          = 0x0E,
  SMB_COM_CREATE_NEW                = 0x0F,
  SMB_COM_CHECK_DIRECTORY           = 0x10,
  SMB_COM_PROCESS_EXIT              = 0x11,
  SMB_COM_SEEK                      = 0x12,
  SMB_COM_LOCK_AND_READ             = 0x13,
  SMB_COM_WRITE_AND_UNLOCK          = 0x14,
  SMB_COM_READ_RAW                  = 0x1A,
  SMB_COM_READ_MPX                  = 0x1B,
  SMB_COM_READ_MPX_SECONDARY        = 0x1C,
  SMB_COM_WRITE_RAW                 = 0x1D,
  SMB_COM_WRITE_MPX                 = 0x1E,
  SMB_COM_WRITE_MPX_SECONDARY       = 0x1F,
  SMB_COM_WRITE_COMPLETE            = 0x20,
  SMB_COM_QUERY_SERVER              = 0x21,
  SMB_COM_SET_INFORMATION2          = 0x22,
  SMB_COM_QUERY_INFORMATION2        = 0x23,
  SMB_COM_LOCKING_ANDX              = 0x24,
  SMB_COM_TRANSACTION               = 0x25,
  SMB_COM_TRANSACTION_SECONDARY     = 0x26,
  SMB_COM_IOCTL                     = 0x27,
  SMB_COM_IOCTL_SECONDARY           = 0x28,
  SMB_COM_COPY                      = 0x29,
  SMB_COM_MOVE                      = 0x2A,
  SMB_COM_ECHO                      = 0x2B,
  SMB_COM_WRITE_AND_CLOSE           = 0x2C,
  SMB_COM_OPEN_ANDX                 = 0x2D,
  SMB_COM_READ_ANDX                 = 0x2E,
  SMB_COM_WRITE_ANDX                = 0x2F,
  SMB_COM_NEW_FILE_SIZE             = 0x30,
  SMB_COM_CLOSE_AND_TREE_DISC       = 0x31,
  SMB_COM_TRANSACTION2              = 0x32,
  SMB_COM_TRANSACTION2_SECONDARY    = 0x33,
  SMB_COM_FIND_CLOSE2               = 0x34,
  SMB_COM_FIND_NOTIFY_CLOSE         = 0x35,
  SMB_COM_TREE_CONNECT              = 0x70,
  SMB_COM_TREE_DISCONNECT           = 0x71,
  SMB_COM_NEGOTIATE                 = 0x72,
  SMB_COM_SESSION_SETUP_ANDX        = 0x73,
  SMB_COM_LOGOFF_ANDX               = 0x74,
  SMB_COM_TREE_CONNECT_ANDX         = 0x75,
  SMB_COM_QUERY_INFORMATION_DISK    = 0x80,
  SMB_COM_SEARCH                    = 0x81,
  SMB_COM_FIND                      = 0x82,
  SMB_COM_FIND_UNIQUE               = 0x83,
  SMB_COM_FIND_CLOSE                = 0x84,
  SMB_COM_NT_TRANSACT               = 0xA0,
  SMB_COM_NT_TRANSACT_SECONDARY     = 0xA1,
  SMB_COM_NT_CREATE_ANDX            = 0xA2,
  SMB_COM_NT_CANCEL                 = 0xA4,
  SMB_COM_NT_RENAME                 = 0xA5,
  SMB_COM_OPEN_PRINT_FILE           = 0xC0,
  SMB_COM_WRITE_PRINT_FILE          = 0xC1,
  SMB_COM_CLOSE_PRINT_FILE          = 0xC2,
  SMB_COM_GET_PRINT_QUEUE           = 0xC3,
  SMB_COM_READ_BULK                 = 0xD8,
  SMB_COM_WRITE_BULK                = 0xD9,
  SMB_COM_WRITE_BULK_DATA           = 0xDA,
  SMB_NO_FURTHER_COMMANDS           = 0xFF
}
for i, v in pairs(command_codes) do
  command_names[v] = i
end
status_codes =
{
  NT_STATUS_SUCCESS                           = 0x00000000,
  NT_STATUS_WERR_BADFILE                      = 0x00000002,
  NT_STATUS_WERR_ACCESS_DENIED                = 0x00000005,
  NT_STATUS_WERR_INVALID_PARAMETER            = 0x00000057,
  NT_STATUS_WERR_INVALID_NAME                 = 0x0000007b,
  NT_STATUS_WERR_UNKNOWN_LEVEL                = 0x0000007c,
  NT_STATUS_WERR_MORE_DATA                    = 0x000000ea,
  NT_STATUS_NO_MORE_ITEMS                     = 0x00000103,
  NT_STATUS_MORE_ENTRIES                      = 0x00000105,
  NT_STATUS_SOME_NOT_MAPPED                   = 0x00000107,
  NT_STATUS_SERVICE_REQUEST_TIMEOUT           = 0x0000041D,
  NT_STATUS_SERVICE_NO_THREAD                 = 0x0000041E,
  NT_STATUS_SERVICE_DATABASE_LOCKED           = 0x0000041F,
  NT_STATUS_SERVICE_ALREADY_RUNNING           = 0x00000420,
  NT_STATUS_INVALID_SERVICE_ACCOUNT           = 0x00000421,
  NT_STATUS_SERVICE_DISABLED                  = 0x00000422,
  NT_STATUS_CIRCULAR_DEPENDENCY               = 0x00000423,
  NT_STATUS_SERVICE_DOES_NOT_EXIST            = 0x00000424,
  NT_STATUS_SERVICE_CANNOT_ACCEPT_CTRL        = 0x00000425,
  NT_STATUS_SERVICE_NOT_ACTIVE                = 0x00000426,
  NT_STATUS_FAILED_SERVICE_CONTROLLER_CONNECT = 0x00000427,
  NT_STATUS_EXCEPTION_IN_SERVICE              = 0x00000428,
  NT_STATUS_DATABASE_DOES_NOT_EXIST           = 0x00000429,
  NT_STATUS_SERVICE_SPECIFIC_ERROR            = 0x0000042a,
  NT_STATUS_PROCESS_ABORTED                   = 0x0000042b,
  NT_STATUS_SERVICE_DEPENDENCY_FAIL           = 0x0000042c,
  NT_STATUS_SERVICE_LOGON_FAILED              = 0x0000042d,
  NT_STATUS_SERVICE_START_HANG                = 0x0000042e,
  NT_STATUS_INVALID_SERVICE_LOCK              = 0x0000042f,
  NT_STATUS_SERVICE_MARKED_FOR_DELETE         = 0x00000430,
  NT_STATUS_SERVICE_EXISTS                    = 0x00000431,
  NT_STATUS_ALREADY_RUNNING_LKG               = 0x00000432,
  NT_STATUS_SERVICE_DEPENDENCY_DELETED        = 0x00000433,
  NT_STATUS_BOOT_ALREADY_ACCEPTED             = 0x00000434,
  NT_STATUS_SERVICE_NEVER_STARTED             = 0x00000435,
  NT_STATUS_DUPLICATE_SERVICE_NAME            = 0x00000436,
  NT_STATUS_DIFFERENT_SERVICE_ACCOUNT         = 0x00000437,
  NT_STATUS_CANNOT_DETECT_DRIVER_FAILURE      = 0x00000438,
  DOS_STATUS_UNKNOWN_ERROR                    = 0x00010001,
  DOS_STATUS_NONSPECIFIC_ERROR                = 0x00010002,
  DOS_STATUS_DIRECTORY_NOT_FOUND              = 0x00030001,
  DOS_STATUS_ACCESS_DENIED                    = 0x00050001,
  DOS_STATUS_INVALID_FID                      = 0x00060001,
  DOS_STATUS_INVALID_NETWORK_NAME             = 0x00060002,
  NT_STATUS_BUFFER_OVERFLOW                   = 0x80000005,
  NT_STATUS_UNSUCCESSFUL                      = 0xc0000001,
  NT_STATUS_NOT_IMPLEMENTED                   = 0xc0000002,
  NT_STATUS_INVALID_INFO_CLASS                = 0xc0000003,
  NT_STATUS_INFO_LENGTH_MISMATCH              = 0xc0000004,
  NT_STATUS_ACCESS_VIOLATION                  = 0xc0000005,
  NT_STATUS_IN_PAGE_ERROR                     = 0xc0000006,
  NT_STATUS_PAGEFILE_QUOTA                    = 0xc0000007,
  NT_STATUS_INVALID_HANDLE                    = 0xc0000008,
  NT_STATUS_BAD_INITIAL_STACK                 = 0xc0000009,
  NT_STATUS_BAD_INITIAL_PC                    = 0xc000000a,
  NT_STATUS_INVALID_CID                       = 0xc000000b,
  NT_STATUS_TIMER_NOT_CANCELED                = 0xc000000c,
  NT_STATUS_INVALID_PARAMETER                 = 0xc000000d,
  NT_STATUS_NO_SUCH_DEVICE                    = 0xc000000e,
  NT_STATUS_NO_SUCH_FILE                      = 0xc000000f,
  NT_STATUS_INVALID_DEVICE_REQUEST            = 0xc0000010,
  NT_STATUS_END_OF_FILE                       = 0xc0000011,
  NT_STATUS_WRONG_VOLUME                      = 0xc0000012,
  NT_STATUS_NO_MEDIA_IN_DEVICE                = 0xc0000013,
  NT_STATUS_UNRECOGNIZED_MEDIA                = 0xc0000014,
  NT_STATUS_NONEXISTENT_SECTOR                = 0xc0000015,
  NT_STATUS_MORE_PROCESSING_REQUIRED          = 0xc0000016,
  NT_STATUS_NO_MEMORY                         = 0xc0000017,
  NT_STATUS_CONFLICTING_ADDRESSES             = 0xc0000018,
  NT_STATUS_NOT_MAPPED_VIEW                   = 0xc0000019,
  NT_STATUS_UNABLE_TO_FREE_VM                 = 0xc000001a,
  NT_STATUS_UNABLE_TO_DELETE_SECTION          = 0xc000001b,
  NT_STATUS_INVALID_SYSTEM_SERVICE            = 0xc000001c,
  NT_STATUS_ILLEGAL_INSTRUCTION               = 0xc000001d,
  NT_STATUS_INVALID_LOCK_SEQUENCE             = 0xc000001e,
  NT_STATUS_INVALID_VIEW_SIZE                 = 0xc000001f,
  NT_STATUS_INVALID_FILE_FOR_SECTION          = 0xc0000020,
  NT_STATUS_ALREADY_COMMITTED                 = 0xc0000021,
  NT_STATUS_ACCESS_DENIED                     = 0xc0000022,
  NT_STATUS_BUFFER_TOO_SMALL                  = 0xc0000023,
  NT_STATUS_OBJECT_TYPE_MISMATCH              = 0xc0000024,
  NT_STATUS_NONCONTINUABLE_EXCEPTION          = 0xc0000025,
  NT_STATUS_INVALID_DISPOSITION               = 0xc0000026,
  NT_STATUS_UNWIND                            = 0xc0000027,
  NT_STATUS_BAD_STACK                         = 0xc0000028,
  NT_STATUS_INVALID_UNWIND_TARGET             = 0xc0000029,
  NT_STATUS_NOT_LOCKED                        = 0xc000002a,
  NT_STATUS_PARITY_ERROR                      = 0xc000002b,
  NT_STATUS_UNABLE_TO_DECOMMIT_VM             = 0xc000002c,
  NT_STATUS_NOT_COMMITTED                     = 0xc000002d,
  NT_STATUS_INVALID_PORT_ATTRIBUTES           = 0xc000002e,
  NT_STATUS_PORT_MESSAGE_TOO_LONG             = 0xc000002f,
  NT_STATUS_INVALID_PARAMETER_MIX             = 0xc0000030,
  NT_STATUS_INVALID_QUOTA_LOWER               = 0xc0000031,
  NT_STATUS_DISK_CORRUPT_ERROR                = 0xc0000032,
  NT_STATUS_OBJECT_NAME_INVALID               = 0xc0000033,
  NT_STATUS_OBJECT_NAME_NOT_FOUND             = 0xc0000034,
  NT_STATUS_OBJECT_NAME_COLLISION             = 0xc0000035,
  NT_STATUS_HANDLE_NOT_WAITABLE               = 0xc0000036,
  NT_STATUS_PORT_DISCONNECTED                 = 0xc0000037,
  NT_STATUS_DEVICE_ALREADY_ATTACHED           = 0xc0000038,
  NT_STATUS_OBJECT_PATH_INVALID               = 0xc0000039,
  NT_STATUS_OBJECT_PATH_NOT_FOUND             = 0xc000003a,
  NT_STATUS_OBJECT_PATH_SYNTAX_BAD            = 0xc000003b,
  NT_STATUS_DATA_OVERRUN                      = 0xc000003c,
  NT_STATUS_DATA_LATE_ERROR                   = 0xc000003d,
  NT_STATUS_DATA_ERROR                        = 0xc000003e,
  NT_STATUS_CRC_ERROR                         = 0xc000003f,
  NT_STATUS_SECTION_TOO_BIG                   = 0xc0000040,
  NT_STATUS_PORT_CONNECTION_REFUSED           = 0xc0000041,
  NT_STATUS_INVALID_PORT_HANDLE               = 0xc0000042,
  NT_STATUS_SHARING_VIOLATION                 = 0xc0000043,
  NT_STATUS_QUOTA_EXCEEDED                    = 0xc0000044,
  NT_STATUS_INVALID_PAGE_PROTECTION           = 0xc0000045,
  NT_STATUS_MUTANT_NOT_OWNED                  = 0xc0000046,
  NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED          = 0xc0000047,
  NT_STATUS_PORT_ALREADY_SET                  = 0xc0000048,
  NT_STATUS_SECTION_NOT_IMAGE                 = 0xc0000049,
  NT_STATUS_SUSPEND_COUNT_EXCEEDED            = 0xc000004a,
  NT_STATUS_THREAD_IS_TERMINATING             = 0xc000004b,
  NT_STATUS_BAD_WORKING_SET_LIMIT             = 0xc000004c,
  NT_STATUS_INCOMPATIBLE_FILE_MAP             = 0xc000004d,
  NT_STATUS_SECTION_PROTECTION                = 0xc000004e,
  NT_STATUS_EAS_NOT_SUPPORTED                 = 0xc000004f,
  NT_STATUS_EA_TOO_LARGE                      = 0xc0000050,
  NT_STATUS_NONEXISTENT_EA_ENTRY              = 0xc0000051,
  NT_STATUS_NO_EAS_ON_FILE                    = 0xc0000052,
  NT_STATUS_EA_CORRUPT_ERROR                  = 0xc0000053,
  NT_STATUS_FILE_LOCK_CONFLICT                = 0xc0000054,
  NT_STATUS_LOCK_NOT_GRANTED                  = 0xc0000055,
  NT_STATUS_DELETE_PENDING                    = 0xc0000056,
  NT_STATUS_CTL_FILE_NOT_SUPPORTED            = 0xc0000057,
  NT_STATUS_UNKNOWN_REVISION                  = 0xc0000058,
  NT_STATUS_REVISION_MISMATCH                 = 0xc0000059,
  NT_STATUS_INVALID_OWNER                     = 0xc000005a,
  NT_STATUS_INVALID_PRIMARY_GROUP             = 0xc000005b,
  NT_STATUS_NO_IMPERSONATION_TOKEN            = 0xc000005c,
  NT_STATUS_CANT_DISABLE_MANDATORY            = 0xc000005d,
  NT_STATUS_NO_LOGON_SERVERS                  = 0xc000005e,
  NT_STATUS_NO_SUCH_LOGON_SESSION             = 0xc000005f,
  NT_STATUS_NO_SUCH_PRIVILEGE                 = 0xc0000060,
  NT_STATUS_PRIVILEGE_NOT_HELD                = 0xc0000061,
  NT_STATUS_INVALID_ACCOUNT_NAME              = 0xc0000062,
  NT_STATUS_USER_EXISTS                       = 0xc0000063,
  NT_STATUS_NO_SUCH_USER                      = 0xc0000064,
  NT_STATUS_GROUP_EXISTS                      = 0xc0000065,
  NT_STATUS_NO_SUCH_GROUP                     = 0xc0000066,
  NT_STATUS_MEMBER_IN_GROUP                   = 0xc0000067,
  NT_STATUS_MEMBER_NOT_IN_GROUP               = 0xc0000068,
  NT_STATUS_LAST_ADMIN                        = 0xc0000069,
  NT_STATUS_WRONG_PASSWORD                    = 0xc000006a,
  NT_STATUS_ILL_FORMED_PASSWORD               = 0xc000006b,
  NT_STATUS_PASSWORD_RESTRICTION              = 0xc000006c,
  NT_STATUS_LOGON_FAILURE                     = 0xc000006d,
  NT_STATUS_ACCOUNT_RESTRICTION               = 0xc000006e,
  NT_STATUS_INVALID_LOGON_HOURS               = 0xc000006f,
  NT_STATUS_INVALID_WORKSTATION               = 0xc0000070,
  NT_STATUS_PASSWORD_EXPIRED                  = 0xc0000071,
  NT_STATUS_ACCOUNT_DISABLED                  = 0xc0000072,
  NT_STATUS_NONE_MAPPED                       = 0xc0000073,
  NT_STATUS_TOO_MANY_LUIDS_REQUESTED          = 0xc0000074,
  NT_STATUS_LUIDS_EXHAUSTED                   = 0xc0000075,
  NT_STATUS_INVALID_SUB_AUTHORITY             = 0xc0000076,
  NT_STATUS_INVALID_ACL                       = 0xc0000077,
  NT_STATUS_INVALID_SID                       = 0xc0000078,
  NT_STATUS_INVALID_SECURITY_DESCR            = 0xc0000079,
  NT_STATUS_PROCEDURE_NOT_FOUND               = 0xc000007a,
  NT_STATUS_INVALID_IMAGE_FORMAT              = 0xc000007b,
  NT_STATUS_NO_TOKEN                          = 0xc000007c,
  NT_STATUS_BAD_INHERITANCE_ACL               = 0xc000007d,
  NT_STATUS_RANGE_NOT_LOCKED                  = 0xc000007e,
  NT_STATUS_DISK_FULL                         = 0xc000007f,
  NT_STATUS_SERVER_DISABLED                   = 0xc0000080,
  NT_STATUS_SERVER_NOT_DISABLED               = 0xc0000081,
  NT_STATUS_TOO_MANY_GUIDS_REQUESTED          = 0xc0000082,
  NT_STATUS_GUIDS_EXHAUSTED                   = 0xc0000083,
  NT_STATUS_INVALID_ID_AUTHORITY              = 0xc0000084,
  NT_STATUS_AGENTS_EXHAUSTED                  = 0xc0000085,
  NT_STATUS_INVALID_VOLUME_LABEL              = 0xc0000086,
  NT_STATUS_SECTION_NOT_EXTENDED              = 0xc0000087,
  NT_STATUS_NOT_MAPPED_DATA                   = 0xc0000088,
  NT_STATUS_RESOURCE_DATA_NOT_FOUND           = 0xc0000089,
  NT_STATUS_RESOURCE_TYPE_NOT_FOUND           = 0xc000008a,
  NT_STATUS_RESOURCE_NAME_NOT_FOUND           = 0xc000008b,
  NT_STATUS_ARRAY_BOUNDS_EXCEEDED             = 0xc000008c,
  NT_STATUS_FLOAT_DENORMAL_OPERAND            = 0xc000008d,
  NT_STATUS_FLOAT_DIVIDE_BY_ZERO              = 0xc000008e,
  NT_STATUS_FLOAT_INEXACT_RESULT              = 0xc000008f,
  NT_STATUS_FLOAT_INVALID_OPERATION           = 0xc0000090,
  NT_STATUS_FLOAT_OVERFLOW                    = 0xc0000091,
  NT_STATUS_FLOAT_STACK_CHECK                 = 0xc0000092,
  NT_STATUS_FLOAT_UNDERFLOW                   = 0xc0000093,
  NT_STATUS_INTEGER_DIVIDE_BY_ZERO            = 0xc0000094,
  NT_STATUS_INTEGER_OVERFLOW                  = 0xc0000095,
  NT_STATUS_PRIVILEGED_INSTRUCTION            = 0xc0000096,
  NT_STATUS_TOO_MANY_PAGING_FILES             = 0xc0000097,
  NT_STATUS_FILE_INVALID                      = 0xc0000098,
  NT_STATUS_ALLOTTED_SPACE_EXCEEDED           = 0xc0000099,
  NT_STATUS_INSUFFICIENT_RESOURCES            = 0xc000009a,
  NT_STATUS_DFS_EXIT_PATH_FOUND               = 0xc000009b,
  NT_STATUS_DEVICE_DATA_ERROR                 = 0xc000009c,
  NT_STATUS_DEVICE_NOT_CONNECTED              = 0xc000009d,
  NT_STATUS_DEVICE_POWER_FAILURE              = 0xc000009e,
  NT_STATUS_FREE_VM_NOT_AT_BASE               = 0xc000009f,
  NT_STATUS_MEMORY_NOT_ALLOCATED              = 0xc00000a0,
  NT_STATUS_WORKING_SET_QUOTA                 = 0xc00000a1,
  NT_STATUS_MEDIA_WRITE_PROTECTED             = 0xc00000a2,
  NT_STATUS_DEVICE_NOT_READY                  = 0xc00000a3,
  NT_STATUS_INVALID_GROUP_ATTRIBUTES          = 0xc00000a4,
  NT_STATUS_BAD_IMPERSONATION_LEVEL           = 0xc00000a5,
  NT_STATUS_CANT_OPEN_ANONYMOUS               = 0xc00000a6,
  NT_STATUS_BAD_VALIDATION_CLASS              = 0xc00000a7,
  NT_STATUS_BAD_TOKEN_TYPE                    = 0xc00000a8,
  NT_STATUS_BAD_MASTER_BOOT_RECORD            = 0xc00000a9,
  NT_STATUS_INSTRUCTION_MISALIGNMENT          = 0xc00000aa,
  NT_STATUS_INSTANCE_NOT_AVAILABLE            = 0xc00000ab,
  NT_STATUS_PIPE_NOT_AVAILABLE                = 0xc00000ac,
  NT_STATUS_INVALID_PIPE_STATE                = 0xc00000ad,
  NT_STATUS_PIPE_BUSY                         = 0xc00000ae,
  NT_STATUS_ILLEGAL_FUNCTION                  = 0xc00000af,
  NT_STATUS_PIPE_DISCONNECTED                 = 0xc00000b0,
  NT_STATUS_PIPE_CLOSING                      = 0xc00000b1,
  NT_STATUS_PIPE_CONNECTED                    = 0xc00000b2,
  NT_STATUS_PIPE_LISTENING                    = 0xc00000b3,
  NT_STATUS_INVALID_READ_MODE                 = 0xc00000b4,
  NT_STATUS_IO_TIMEOUT                        = 0xc00000b5,
  NT_STATUS_FILE_FORCED_CLOSED                = 0xc00000b6,
  NT_STATUS_PROFILING_NOT_STARTED             = 0xc00000b7,
  NT_STATUS_PROFILING_NOT_STOPPED             = 0xc00000b8,
  NT_STATUS_COULD_NOT_INTERPRET               = 0xc00000b9,
  NT_STATUS_FILE_IS_A_DIRECTORY               = 0xc00000ba,
  NT_STATUS_NOT_SUPPORTED                     = 0xc00000bb,
  NT_STATUS_REMOTE_NOT_LISTENING              = 0xc00000bc,
  NT_STATUS_DUPLICATE_NAME                    = 0xc00000bd,
  NT_STATUS_BAD_NETWORK_PATH                  = 0xc00000be,
  NT_STATUS_NETWORK_BUSY                      = 0xc00000bf,
  NT_STATUS_DEVICE_DOES_NOT_EXIST             = 0xc00000c0,
  NT_STATUS_TOO_MANY_COMMANDS                 = 0xc00000c1,
  NT_STATUS_ADAPTER_HARDWARE_ERROR            = 0xc00000c2,
  NT_STATUS_INVALID_NETWORK_RESPONSE          = 0xc00000c3,
  NT_STATUS_UNEXPECTED_NETWORK_ERROR          = 0xc00000c4,
  NT_STATUS_BAD_REMOTE_ADAPTER                = 0xc00000c5,
  NT_STATUS_PRINT_QUEUE_FULL                  = 0xc00000c6,
  NT_STATUS_NO_SPOOL_SPACE                    = 0xc00000c7,
  NT_STATUS_PRINT_CANCELLED                   = 0xc00000c8,
  NT_STATUS_NETWORK_NAME_DELETED              = 0xc00000c9,
  NT_STATUS_NETWORK_ACCESS_DENIED             = 0xc00000ca,
  NT_STATUS_BAD_DEVICE_TYPE                   = 0xc00000cb,
  NT_STATUS_BAD_NETWORK_NAME                  = 0xc00000cc,
  NT_STATUS_TOO_MANY_NAMES                    = 0xc00000cd,
  NT_STATUS_TOO_MANY_SESSIONS                 = 0xc00000ce,
  NT_STATUS_SHARING_PAUSED                    = 0xc00000cf,
  NT_STATUS_REQUEST_NOT_ACCEPTED              = 0xc00000d0,
  NT_STATUS_REDIRECTOR_PAUSED                 = 0xc00000d1,
  NT_STATUS_NET_WRITE_FAULT                   = 0xc00000d2,
  NT_STATUS_PROFILING_AT_LIMIT                = 0xc00000d3,
  NT_STATUS_NOT_SAME_DEVICE                   = 0xc00000d4,
  NT_STATUS_FILE_RENAMED                      = 0xc00000d5,
  NT_STATUS_VIRTUAL_CIRCUIT_CLOSED            = 0xc00000d6,
  NT_STATUS_NO_SECURITY_ON_OBJECT             = 0xc00000d7,
  NT_STATUS_CANT_WAIT                         = 0xc00000d8,
  NT_STATUS_PIPE_EMPTY                        = 0xc00000d9,
  NT_STATUS_CANT_ACCESS_DOMAIN_INFO           = 0xc00000da,
  NT_STATUS_CANT_TERMINATE_SELF               = 0xc00000db,
  NT_STATUS_INVALID_SERVER_STATE              = 0xc00000dc,
  NT_STATUS_INVALID_DOMAIN_STATE              = 0xc00000dd,
  NT_STATUS_INVALID_DOMAIN_ROLE               = 0xc00000de,
  NT_STATUS_NO_SUCH_DOMAIN                    = 0xc00000df,
  NT_STATUS_DOMAIN_EXISTS                     = 0xc00000e0,
  NT_STATUS_DOMAIN_LIMIT_EXCEEDED             = 0xc00000e1,
  NT_STATUS_OPLOCK_NOT_GRANTED                = 0xc00000e2,
  NT_STATUS_INVALID_OPLOCK_PROTOCOL           = 0xc00000e3,
  NT_STATUS_INTERNAL_DB_CORRUPTION            = 0xc00000e4,
  NT_STATUS_INTERNAL_ERROR                    = 0xc00000e5,
  NT_STATUS_GENERIC_NOT_MAPPED                = 0xc00000e6,
  NT_STATUS_BAD_DESCRIPTOR_FORMAT             = 0xc00000e7,
  NT_STATUS_INVALID_USER_BUFFER               = 0xc00000e8,
  NT_STATUS_UNEXPECTED_IO_ERROR               = 0xc00000e9,
  NT_STATUS_UNEXPECTED_MM_CREATE_ERR          = 0xc00000ea,
  NT_STATUS_UNEXPECTED_MM_MAP_ERROR           = 0xc00000eb,
  NT_STATUS_UNEXPECTED_MM_EXTEND_ERR          = 0xc00000ec,
  NT_STATUS_NOT_LOGON_PROCESS                 = 0xc00000ed,
  NT_STATUS_LOGON_SESSION_EXISTS              = 0xc00000ee,
  NT_STATUS_INVALID_PARAMETER_1               = 0xc00000ef,
  NT_STATUS_INVALID_PARAMETER_2               = 0xc00000f0,
  NT_STATUS_INVALID_PARAMETER_3               = 0xc00000f1,
  NT_STATUS_INVALID_PARAMETER_4               = 0xc00000f2,
  NT_STATUS_INVALID_PARAMETER_5               = 0xc00000f3,
  NT_STATUS_INVALID_PARAMETER_6               = 0xc00000f4,
  NT_STATUS_INVALID_PARAMETER_7               = 0xc00000f5,
  NT_STATUS_INVALID_PARAMETER_8               = 0xc00000f6,
  NT_STATUS_INVALID_PARAMETER_9               = 0xc00000f7,
  NT_STATUS_INVALID_PARAMETER_10              = 0xc00000f8,
  NT_STATUS_INVALID_PARAMETER_11              = 0xc00000f9,
  NT_STATUS_INVALID_PARAMETER_12              = 0xc00000fa,
  NT_STATUS_REDIRECTOR_NOT_STARTED            = 0xc00000fb,
  NT_STATUS_REDIRECTOR_STARTED                = 0xc00000fc,
  NT_STATUS_STACK_OVERFLOW                    = 0xc00000fd,
  NT_STATUS_NO_SUCH_PACKAGE                   = 0xc00000fe,
  NT_STATUS_BAD_FUNCTION_TABLE                = 0xc00000ff,
  NT_STATUS_DIRECTORY_NOT_EMPTY               = 0xc0000101,
  NT_STATUS_FILE_CORRUPT_ERROR                = 0xc0000102,
  NT_STATUS_NOT_A_DIRECTORY                   = 0xc0000103,
  NT_STATUS_BAD_LOGON_SESSION_STATE           = 0xc0000104,
  NT_STATUS_LOGON_SESSION_COLLISION           = 0xc0000105,
  NT_STATUS_NAME_TOO_LONG                     = 0xc0000106,
  NT_STATUS_FILES_OPEN                        = 0xc0000107,
  NT_STATUS_CONNECTION_IN_USE                 = 0xc0000108,
  NT_STATUS_MESSAGE_NOT_FOUND                 = 0xc0000109,
  NT_STATUS_PROCESS_IS_TERMINATING            = 0xc000010a,
  NT_STATUS_INVALID_LOGON_TYPE                = 0xc000010b,
  NT_STATUS_NO_GUID_TRANSLATION               = 0xc000010c,
  NT_STATUS_CANNOT_IMPERSONATE                = 0xc000010d,
  NT_STATUS_IMAGE_ALREADY_LOADED              = 0xc000010e,
  NT_STATUS_ABIOS_NOT_PRESENT                 = 0xc000010f,
  NT_STATUS_ABIOS_LID_NOT_EXIST               = 0xc0000110,
  NT_STATUS_ABIOS_LID_ALREADY_OWNED           = 0xc0000111,
  NT_STATUS_ABIOS_NOT_LID_OWNER               = 0xc0000112,
  NT_STATUS_ABIOS_INVALID_COMMAND             = 0xc0000113,
  NT_STATUS_ABIOS_INVALID_LID                 = 0xc0000114,
  NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE      = 0xc0000115,
  NT_STATUS_ABIOS_INVALID_SELECTOR            = 0xc0000116,
  NT_STATUS_NO_LDT                            = 0xc0000117,
  NT_STATUS_INVALID_LDT_SIZE                  = 0xc0000118,
  NT_STATUS_INVALID_LDT_OFFSET                = 0xc0000119,
  NT_STATUS_INVALID_LDT_DESCRIPTOR            = 0xc000011a,
  NT_STATUS_INVALID_IMAGE_NE_FORMAT           = 0xc000011b,
  NT_STATUS_RXACT_INVALID_STATE               = 0xc000011c,
  NT_STATUS_RXACT_COMMIT_FAILURE              = 0xc000011d,
  NT_STATUS_MAPPED_FILE_SIZE_ZERO             = 0xc000011e,
  NT_STATUS_TOO_MANY_OPENED_FILES             = 0xc000011f,
  NT_STATUS_CANCELLED                         = 0xc0000120,
  NT_STATUS_CANNOT_DELETE                     = 0xc0000121,
  NT_STATUS_INVALID_COMPUTER_NAME             = 0xc0000122,
  NT_STATUS_FILE_DELETED                      = 0xc0000123,
  NT_STATUS_SPECIAL_ACCOUNT                   = 0xc0000124,
  NT_STATUS_SPECIAL_GROUP                     = 0xc0000125,
  NT_STATUS_SPECIAL_USER                      = 0xc0000126,
  NT_STATUS_MEMBERS_PRIMARY_GROUP             = 0xc0000127,
  NT_STATUS_FILE_CLOSED                       = 0xc0000128,
  NT_STATUS_TOO_MANY_THREADS                  = 0xc0000129,
  NT_STATUS_THREAD_NOT_IN_PROCESS             = 0xc000012a,
  NT_STATUS_TOKEN_ALREADY_IN_USE              = 0xc000012b,
  NT_STATUS_PAGEFILE_QUOTA_EXCEEDED           = 0xc000012c,
  NT_STATUS_COMMITMENT_LIMIT                  = 0xc000012d,
  NT_STATUS_INVALID_IMAGE_LE_FORMAT           = 0xc000012e,
  NT_STATUS_INVALID_IMAGE_NOT_MZ              = 0xc000012f,
  NT_STATUS_INVALID_IMAGE_PROTECT             = 0xc0000130,
  NT_STATUS_INVALID_IMAGE_WIN_16              = 0xc0000131,
  NT_STATUS_LOGON_SERVER_CONFLICT             = 0xc0000132,
  NT_STATUS_TIME_DIFFERENCE_AT_DC             = 0xc0000133,
  NT_STATUS_SYNCHRONIZATION_REQUIRED          = 0xc0000134,
  NT_STATUS_DLL_NOT_FOUND                     = 0xc0000135,
  NT_STATUS_OPEN_FAILED                       = 0xc0000136,
  NT_STATUS_IO_PRIVILEGE_FAILED               = 0xc0000137,
  NT_STATUS_ORDINAL_NOT_FOUND                 = 0xc0000138,
  NT_STATUS_ENTRYPOINT_NOT_FOUND              = 0xc0000139,
  NT_STATUS_CONTROL_C_EXIT                    = 0xc000013a,
  NT_STATUS_LOCAL_DISCONNECT                  = 0xc000013b,
  NT_STATUS_REMOTE_DISCONNECT                 = 0xc000013c,
  NT_STATUS_REMOTE_RESOURCES                  = 0xc000013d,
  NT_STATUS_LINK_FAILED                       = 0xc000013e,
  NT_STATUS_LINK_TIMEOUT                      = 0xc000013f,
  NT_STATUS_INVALID_CONNECTION                = 0xc0000140,
  NT_STATUS_INVALID_ADDRESS                   = 0xc0000141,
  NT_STATUS_DLL_INIT_FAILED                   = 0xc0000142,
  NT_STATUS_MISSING_SYSTEMFILE                = 0xc0000143,
  NT_STATUS_UNHANDLED_EXCEPTION               = 0xc0000144,
  NT_STATUS_APP_INIT_FAILURE                  = 0xc0000145,
  NT_STATUS_PAGEFILE_CREATE_FAILED            = 0xc0000146,
  NT_STATUS_NO_PAGEFILE                       = 0xc0000147,
  NT_STATUS_INVALID_LEVEL                     = 0xc0000148,
  NT_STATUS_WRONG_PASSWORD_CORE               = 0xc0000149,
  NT_STATUS_ILLEGAL_FLOAT_CONTEXT             = 0xc000014a,
  NT_STATUS_PIPE_BROKEN                       = 0xc000014b,
  NT_STATUS_REGISTRY_CORRUPT                  = 0xc000014c,
  NT_STATUS_REGISTRY_IO_FAILED                = 0xc000014d,
  NT_STATUS_NO_EVENT_PAIR                     = 0xc000014e,
  NT_STATUS_UNRECOGNIZED_VOLUME               = 0xc000014f,
  NT_STATUS_SERIAL_NO_DEVICE_INITED           = 0xc0000150,
  NT_STATUS_NO_SUCH_ALIAS                     = 0xc0000151,
  NT_STATUS_MEMBER_NOT_IN_ALIAS               = 0xc0000152,
  NT_STATUS_MEMBER_IN_ALIAS                   = 0xc0000153,
  NT_STATUS_ALIAS_EXISTS                      = 0xc0000154,
  NT_STATUS_LOGON_NOT_GRANTED                 = 0xc0000155,
  NT_STATUS_TOO_MANY_SECRETS                  = 0xc0000156,
  NT_STATUS_SECRET_TOO_LONG                   = 0xc0000157,
  NT_STATUS_INTERNAL_DB_ERROR                 = 0xc0000158,
  NT_STATUS_FULLSCREEN_MODE                   = 0xc0000159,
  NT_STATUS_TOO_MANY_CONTEXT_IDS              = 0xc000015a,
  NT_STATUS_LOGON_TYPE_NOT_GRANTED            = 0xc000015b,
  NT_STATUS_NOT_REGISTRY_FILE                 = 0xc000015c,
  NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED      = 0xc000015d,
  NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR         = 0xc000015e,
  NT_STATUS_FT_MISSING_MEMBER                 = 0xc000015f,
  NT_STATUS_ILL_FORMED_SERVICE_ENTRY          = 0xc0000160,
  NT_STATUS_ILLEGAL_CHARACTER                 = 0xc0000161,
  NT_STATUS_UNMAPPABLE_CHARACTER              = 0xc0000162,
  NT_STATUS_UNDEFINED_CHARACTER               = 0xc0000163,
  NT_STATUS_FLOPPY_VOLUME                     = 0xc0000164,
  NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND          = 0xc0000165,
  NT_STATUS_FLOPPY_WRONG_CYLINDER             = 0xc0000166,
  NT_STATUS_FLOPPY_UNKNOWN_ERROR              = 0xc0000167,
  NT_STATUS_FLOPPY_BAD_REGISTERS              = 0xc0000168,
  NT_STATUS_DISK_RECALIBRATE_FAILED           = 0xc0000169,
  NT_STATUS_DISK_OPERATION_FAILED             = 0xc000016a,
  NT_STATUS_DISK_RESET_FAILED                 = 0xc000016b,
  NT_STATUS_SHARED_IRQ_BUSY                   = 0xc000016c,
  NT_STATUS_FT_ORPHANING                      = 0xc000016d,
  NT_STATUS_PARTITION_FAILURE                 = 0xc0000172,
  NT_STATUS_INVALID_BLOCK_LENGTH              = 0xc0000173,
  NT_STATUS_DEVICE_NOT_PARTITIONED            = 0xc0000174,
  NT_STATUS_UNABLE_TO_LOCK_MEDIA              = 0xc0000175,
  NT_STATUS_UNABLE_TO_UNLOAD_MEDIA            = 0xc0000176,
  NT_STATUS_EOM_OVERFLOW                      = 0xc0000177,
  NT_STATUS_NO_MEDIA                          = 0xc0000178,
  NT_STATUS_NO_SUCH_MEMBER                    = 0xc000017a,
  NT_STATUS_INVALID_MEMBER                    = 0xc000017b,
  NT_STATUS_KEY_DELETED                       = 0xc000017c,
  NT_STATUS_NO_LOG_SPACE                      = 0xc000017d,
  NT_STATUS_TOO_MANY_SIDS                     = 0xc000017e,
  NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED      = 0xc000017f,
  NT_STATUS_KEY_HAS_CHILDREN                  = 0xc0000180,
  NT_STATUS_CHILD_MUST_BE_VOLATILE            = 0xc0000181,
  NT_STATUS_DEVICE_CONFIGURATION_ERROR        = 0xc0000182,
  NT_STATUS_DRIVER_INTERNAL_ERROR             = 0xc0000183,
  NT_STATUS_INVALID_DEVICE_STATE              = 0xc0000184,
  NT_STATUS_IO_DEVICE_ERROR                   = 0xc0000185,
  NT_STATUS_DEVICE_PROTOCOL_ERROR             = 0xc0000186,
  NT_STATUS_BACKUP_CONTROLLER                 = 0xc0000187,
  NT_STATUS_LOG_FILE_FULL                     = 0xc0000188,
  NT_STATUS_TOO_LATE                          = 0xc0000189,
  NT_STATUS_NO_TRUST_LSA_SECRET               = 0xc000018a,
  NT_STATUS_NO_TRUST_SAM_ACCOUNT              = 0xc000018b,
  NT_STATUS_TRUSTED_DOMAIN_FAILURE            = 0xc000018c,
  NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE      = 0xc000018d,
  NT_STATUS_EVENTLOG_FILE_CORRUPT             = 0xc000018e,
  NT_STATUS_EVENTLOG_CANT_START               = 0xc000018f,
  NT_STATUS_TRUST_FAILURE                     = 0xc0000190,
  NT_STATUS_MUTANT_LIMIT_EXCEEDED             = 0xc0000191,
  NT_STATUS_NETLOGON_NOT_STARTED              = 0xc0000192,
  NT_STATUS_ACCOUNT_EXPIRED                   = 0xc0000193,
  NT_STATUS_POSSIBLE_DEADLOCK                 = 0xc0000194,
  NT_STATUS_NETWORK_CREDENTIAL_CONFLICT       = 0xc0000195,
  NT_STATUS_REMOTE_SESSION_LIMIT              = 0xc0000196,
  NT_STATUS_EVENTLOG_FILE_CHANGED             = 0xc0000197,
  NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xc0000198,
  NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xc0000199,
  NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT      = 0xc000019a,
  NT_STATUS_DOMAIN_TRUST_INCONSISTENT         = 0xc000019b,
  NT_STATUS_FS_DRIVER_REQUIRED                = 0xc000019c,
  NT_STATUS_NO_USER_SESSION_KEY               = 0xc0000202,
  NT_STATUS_USER_SESSION_DELETED              = 0xc0000203,
  NT_STATUS_RESOURCE_LANG_NOT_FOUND           = 0xc0000204,
  NT_STATUS_INSUFF_SERVER_RESOURCES           = 0xc0000205,
  NT_STATUS_INVALID_BUFFER_SIZE               = 0xc0000206,
  NT_STATUS_INVALID_ADDRESS_COMPONENT         = 0xc0000207,
  NT_STATUS_INVALID_ADDRESS_WILDCARD          = 0xc0000208,
  NT_STATUS_TOO_MANY_ADDRESSES                = 0xc0000209,
  NT_STATUS_ADDRESS_ALREADY_EXISTS            = 0xc000020a,
  NT_STATUS_ADDRESS_CLOSED                    = 0xc000020b,
  NT_STATUS_CONNECTION_DISCONNECTED           = 0xc000020c,
  NT_STATUS_CONNECTION_RESET                  = 0xc000020d,
  NT_STATUS_TOO_MANY_NODES                    = 0xc000020e,
  NT_STATUS_TRANSACTION_ABORTED               = 0xc000020f,
  NT_STATUS_TRANSACTION_TIMED_OUT             = 0xc0000210,
  NT_STATUS_TRANSACTION_NO_RELEASE            = 0xc0000211,
  NT_STATUS_TRANSACTION_NO_MATCH              = 0xc0000212,
  NT_STATUS_TRANSACTION_RESPONDED             = 0xc0000213,
  NT_STATUS_TRANSACTION_INVALID_ID            = 0xc0000214,
  NT_STATUS_TRANSACTION_INVALID_TYPE          = 0xc0000215,
  NT_STATUS_NOT_SERVER_SESSION                = 0xc0000216,
  NT_STATUS_NOT_CLIENT_SESSION                = 0xc0000217,
  NT_STATUS_CANNOT_LOAD_REGISTRY_FILE         = 0xc0000218,
  NT_STATUS_DEBUG_ATTACH_FAILED               = 0xc0000219,
  NT_STATUS_SYSTEM_PROCESS_TERMINATED         = 0xc000021a,
  NT_STATUS_DATA_NOT_ACCEPTED                 = 0xc000021b,
  NT_STATUS_NO_BROWSER_SERVERS_FOUND          = 0xc000021c,
  NT_STATUS_VDM_HARD_ERROR                    = 0xc000021d,
  NT_STATUS_DRIVER_CANCEL_TIMEOUT             = 0xc000021e,
  NT_STATUS_REPLY_MESSAGE_MISMATCH            = 0xc000021f,
  NT_STATUS_MAPPED_ALIGNMENT                  = 0xc0000220,
  NT_STATUS_IMAGE_CHECKSUM_MISMATCH           = 0xc0000221,
  NT_STATUS_LOST_WRITEBEHIND_DATA             = 0xc0000222,
  NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID  = 0xc0000223,
  NT_STATUS_PASSWORD_MUST_CHANGE              = 0xc0000224,
  NT_STATUS_NOT_FOUND                         = 0xc0000225,
  NT_STATUS_NOT_TINY_STREAM                   = 0xc0000226,
  NT_STATUS_RECOVERY_FAILURE                  = 0xc0000227,
  NT_STATUS_STACK_OVERFLOW_READ               = 0xc0000228,
  NT_STATUS_FAIL_CHECK                        = 0xc0000229,
  NT_STATUS_DUPLICATE_OBJECTID                = 0xc000022a,
  NT_STATUS_OBJECTID_EXISTS                   = 0xc000022b,
  NT_STATUS_CONVERT_TO_LARGE                  = 0xc000022c,
  NT_STATUS_RETRY                             = 0xc000022d,
  NT_STATUS_FOUND_OUT_OF_SCOPE                = 0xc000022e,
  NT_STATUS_ALLOCATE_BUCKET                   = 0xc000022f,
  NT_STATUS_PROPSET_NOT_FOUND                 = 0xc0000230,
  NT_STATUS_MARSHALL_OVERFLOW                 = 0xc0000231,
  NT_STATUS_INVALID_VARIANT                   = 0xc0000232,
  NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND       = 0xc0000233,
  NT_STATUS_ACCOUNT_LOCKED_OUT                = 0xc0000234,
  NT_STATUS_HANDLE_NOT_CLOSABLE               = 0xc0000235,
  NT_STATUS_CONNECTION_REFUSED                = 0xc0000236,
  NT_STATUS_GRACEFUL_DISCONNECT               = 0xc0000237,
  NT_STATUS_ADDRESS_ALREADY_ASSOCIATED        = 0xc0000238,
  NT_STATUS_ADDRESS_NOT_ASSOCIATED            = 0xc0000239,
  NT_STATUS_CONNECTION_INVALID                = 0xc000023a,
  NT_STATUS_CONNECTION_ACTIVE                 = 0xc000023b,
  NT_STATUS_NETWORK_UNREACHABLE               = 0xc000023c,
  NT_STATUS_HOST_UNREACHABLE                  = 0xc000023d,
  NT_STATUS_PROTOCOL_UNREACHABLE              = 0xc000023e,
  NT_STATUS_PORT_UNREACHABLE                  = 0xc000023f,
  NT_STATUS_REQUEST_ABORTED                   = 0xc0000240,
  NT_STATUS_CONNECTION_ABORTED                = 0xc0000241,
  NT_STATUS_BAD_COMPRESSION_BUFFER            = 0xc0000242,
  NT_STATUS_USER_MAPPED_FILE                  = 0xc0000243,
  NT_STATUS_AUDIT_FAILED                      = 0xc0000244,
  NT_STATUS_TIMER_RESOLUTION_NOT_SET          = 0xc0000245,
  NT_STATUS_CONNECTION_COUNT_LIMIT            = 0xc0000246,
  NT_STATUS_LOGIN_TIME_RESTRICTION            = 0xc0000247,
  NT_STATUS_LOGIN_WKSTA_RESTRICTION           = 0xc0000248,
  NT_STATUS_IMAGE_MP_UP_MISMATCH              = 0xc0000249,
  NT_STATUS_INSUFFICIENT_LOGON_INFO           = 0xc0000250,
  NT_STATUS_BAD_DLL_ENTRYPOINT                = 0xc0000251,
  NT_STATUS_BAD_SERVICE_ENTRYPOINT            = 0xc0000252,
  NT_STATUS_LPC_REPLY_LOST                    = 0xc0000253,
  NT_STATUS_IP_ADDRESS_CONFLICT1              = 0xc0000254,
  NT_STATUS_IP_ADDRESS_CONFLICT2              = 0xc0000255,
  NT_STATUS_REGISTRY_QUOTA_LIMIT              = 0xc0000256,
  NT_STATUS_PATH_NOT_COVERED                  = 0xc0000257,
  NT_STATUS_NO_CALLBACK_ACTIVE                = 0xc0000258,
  NT_STATUS_LICENSE_QUOTA_EXCEEDED            = 0xc0000259,
  NT_STATUS_PWD_TOO_SHORT                     = 0xc000025a,
  NT_STATUS_PWD_TOO_RECENT                    = 0xc000025b,
  NT_STATUS_PWD_HISTORY_CONFLICT              = 0xc000025c,
  NT_STATUS_PLUGPLAY_NO_DEVICE                = 0xc000025e,
  NT_STATUS_UNSUPPORTED_COMPRESSION           = 0xc000025f,
  NT_STATUS_INVALID_HW_PROFILE                = 0xc0000260,
  NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH      = 0xc0000261,
  NT_STATUS_DRIVER_ORDINAL_NOT_FOUND          = 0xc0000262,
  NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND       = 0xc0000263,
  NT_STATUS_RESOURCE_NOT_OWNED                = 0xc0000264,
  NT_STATUS_TOO_MANY_LINKS                    = 0xc0000265,
  NT_STATUS_QUOTA_LIST_INCONSISTENT           = 0xc0000266,
  NT_STATUS_FILE_IS_OFFLINE                   = 0xc0000267,
  NT_STATUS_DS_NO_MORE_RIDS                   = 0xc00002a8,
  NT_STATUS_NOT_A_REPARSE_POINT               = 0xc0000275,
  NT_STATUS_NO_SUCH_JOB                       = 0xc000EDE
}
for i, v in pairs(status_codes) do
  status_names[v] = i
end
local NP_LIBRARY_NAME = "PIPE"
namedpipes =
{
  get_pipe_subpath = function( pipeName, writeToDebugLog )
    local status, pipeSubPath
    if not pipeName then return false end
    local _, _, match = pipeName:match( "^(\\+)(.-)\\pipe(\\.-)$" )
    if match then
      pipeSubPath = match
      status = true
      if writeToDebugLog then
        stdnse.debug2("%s: Converting %s to subpath %s", NP_LIBRARY_NAME, pipeName, match )
      end
    else
      status = false
      pipeSubPath = pipeName
    end
    return status, pipeSubPath
  end,
  make_pipe_name = function( hostnameOrIp, pipeSubPath )
    if pipeSubPath:sub(1,1) ~= "\\" then
      pipeSubPath = "\\" .. pipeSubPath
    end
    return string.format( "\\\\%s\\pipe%s", hostnameOrIp, pipeSubPath )
  end,
  named_pipe = {
    _smbstate = nil,
    _host = nil,
    _pipeSubPath = nil,
    _overrides = nil,
    name = nil,
    new = function(self,o)
      o = o or {}
      setmetatable(o, self)
      self.__index = self
      return o
    end,
    connect = function( self, host, pipeSubPath, overrides )
      stdnse.debug2("%s: connect() called with %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) )
      self._overrides = overrides or {}
      self._host = host
      self._pipeSubPath = pipeSubPath
      if not host and not host.ip then return false, "host table is required" end
      if not pipeSubPath then return false, "pipeSubPath is required" end
      if ( pipeSubPath:match( "^\\\\(.-)$" ) ) then
        local status
        status, self._pipeSubPath = namedpipes.get_pipe_subpath( self._pipeSubPath, true )
        if ( not status ) then
          stdnse.debug1("%s: Attempt to connect to invalid pipe name: %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) )
          return false, "Invalid pipe name"
        end
      end
      self.name = namedpipes.make_pipe_name( self._host.ip, self._pipeSubPath )
      stdnse.debug2("%s: Connecting to named pipe: %s", NP_LIBRARY_NAME, self.name )
      local errorMessage
      local bool_negotiate_protocol, bool_start_session, bool_disable_extended = true, true, false
      local _, fqpn_share = get_fqpn(host, "IPC$")
      local status, result = start_ex( self._host, bool_negotiate_protocol, bool_start_session,
        fqpn_share, self._pipeSubPath, bool_disable_extended, self._overrides )
      if status then
        self._smbstate = result
      else
        errorMessage = string.format( "Connection failed: %s", result )
        stdnse.debug2("%s: Connection to named pipe (%s) failed: %s",
          NP_LIBRARY_NAME, self.name, errorMessage )
      end
      return status, errorMessage, result
    end,
    disconnect = function( self )
      if ( self._smbstate ) then
        stdnse.debug2("%s: Disconnecting named pipe: %s", NP_LIBRARY_NAME, self.name )
        return stop( self._smbstate )
      else
        stdnse.debug2("%s: disconnect() called, but SMB connection is already closed: %s", NP_LIBRARY_NAME, self.name )
      end
    end,
    send = function( self, messageData )
      if not self._smbstate then
        stdnse.debug2("%s: send() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name )
        return false, "Failed to send message on named pipe"
      end
      local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity
      local status, result, errorMessage
      status, result = write_file( self._smbstate, messageData, offset, self._overrides )
      if not status then
        stdnse.debug2("%s: Write to named pipe (%s) failed: %s",
          NP_LIBRARY_NAME, self.name, result )
        errorMessage = "Failed to send message on named pipe", result
      end
      return status, errorMessage
    end,
    receive = function( self )
      if not self._smbstate then
        stdnse.debug2("%s: receive() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name )
        return false, "Failed to read from named pipe"
      end
      local status, result, messageData
      local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity
      local MAX_BYTES_PER_READ = 4096
      status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides )
      if status and result.data then
        messageData = result.data
      else
        stdnse.debug2("%s: Read from named pipe (%s) failed: %s",
          NP_LIBRARY_NAME, self.name, result )
        return false, "Failed to read from named pipe", result
      end
      while (result["status"] == status_codes.NT_STATUS_BUFFER_OVERFLOW) do
        status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides )
        if status and result.data then
          messageData = messageData .. result.data
        else
          stdnse.debug2("%s: Read additional data from named pipe (%s) failed: %s",
            NP_LIBRARY_NAME, self.name, result )
          return false, "Failed to read from named pipe", result
        end
      end
      return status, messageData
    end,
  }
}
filetype_codes =
{
  FILE_TYPE_DISK              = 0x00,
  FILE_TYPE_BYTE_MODE_PIPE    = 0x01,
  FILE_TYPE_MESSAGE_MODE_PIPE = 0x02,
  FILE_TYPE_PRINTER           = 0x03,
  FILE_TYPE_UNKNOWN           = 0xFF
}
for i, v in pairs(filetype_codes) do
  filetype_names[v] = i
end
return _ENV;
