# Copyright (c) 2003-2016 CORE Security Technologies
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Author: Alberto Solino (@agsolino)
#
# TODO:
# [-] Functions should return NT error codes
# [-] Handling errors in all situations, right now it's just raising exceptions.
# [*] Standard authentication support
# [ ] Organize the connectionData stuff
# [*] Add capability to send a bad user ID if the user is not authenticated,
#     right now you can ask for any command without actually being authenticated
# [ ] PATH TRAVERSALS EVERYWHERE.. BE WARNED!
# [ ] Check the credentials.. now we're just letting everybody to log in.
# [ ] Check error situation (now many places assume the right data is coming)
# [ ] Implement IPC to the main process so the connectionData is on a single place
# [ ] Hence.. implement locking
# estamos en la B

from __future__ import with_statement
import calendar
import socket
import time
import datetime
import struct
try: # Python 3
    import configparser
except ImportError: # Python 2
    import ConfigParser as configparser
import SocketServer
import threading
import logging
import logging.config
import ntpath
import os
import fnmatch
import errno
import sys
import random
import shutil
from binascii import hexlify

# For signing
from impacket import smb, nmb, ntlm, uuid, LOG
from impacket import smb3structs as smb2
from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, MechTypes, SPNEGO_NegTokenResp, ASN1_AID, ASN1_SUPPORTED_MECH
from impacket.nt_errors import STATUS_NO_MORE_FILES, STATUS_NETWORK_NAME_DELETED, STATUS_INVALID_PARAMETER, \
    STATUS_FILE_CLOSED, STATUS_MORE_PROCESSING_REQUIRED, STATUS_OBJECT_PATH_NOT_FOUND, STATUS_DIRECTORY_NOT_EMPTY, \
    STATUS_FILE_IS_A_DIRECTORY, STATUS_NOT_IMPLEMENTED, STATUS_INVALID_HANDLE, STATUS_OBJECT_NAME_COLLISION, \
    STATUS_NO_SUCH_FILE, STATUS_CANCELLED, STATUS_OBJECT_NAME_NOT_FOUND, STATUS_SUCCESS, STATUS_ACCESS_DENIED, \
    STATUS_NOT_SUPPORTED, STATUS_INVALID_DEVICE_REQUEST, STATUS_FS_DRIVER_REQUIRED, STATUS_INVALID_INFO_CLASS

# These ones not defined in nt_errors
STATUS_SMB_BAD_UID = 0x005B0002
STATUS_SMB_BAD_TID = 0x00050002

try:
    unicode        # Python 2
except NameError:
    unicode = str  # Python 3


# Utility functions
# and general functions.
# There are some common functions that can be accessed from more than one SMB
# command (or either TRANSACTION). That's why I'm putting them here
# TODO: Return NT ERROR Codes

def outputToJohnFormat(challenge, username, domain, lmresponse, ntresponse):
# We don't want to add a possible failure here, since this is an
# extra bonus. We try, if it fails, returns nothing
    ret_value = ''
    try:
        if len(ntresponse) > 24:
            # Extended Security - NTLMv2
            ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'}
        else:
            # NTLMv1
            ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username.decode('utf-16le'), domain.decode('utf-16le'), hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'}
    except:
        # Let's try w/o decoding Unicode
        try:
            if len(ntresponse) > 24:
                # Extended Security - NTLMv2
                ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(challenge), hexlify(ntresponse)[:32], hexlify(ntresponse)[32:]), 'hash_version':'ntlmv2'}
            else:
                # NTLMv1
                ret_value = {'hash_string':'%s::%s:%s:%s:%s' % (username, domain, hexlify(lmresponse), hexlify(ntresponse), hexlify(challenge)), 'hash_version':'ntlm'}
        except Exception as e:
            LOG.error("outputToJohnFormat: %s" % e)
            pass

    return ret_value

def writeJohnOutputToFile(hash_string, hash_version, file_name):
    fn_data = os.path.splitext(file_name)
    if hash_version == "ntlmv2":
        output_filename = fn_data[0] + "_ntlmv2" + fn_data[1]
    else:
        output_filename = fn_data[0] + "_ntlm" + fn_data[1]

    with open(output_filename,"a") as f:
            f.write(hash_string)
            f.write('\n')


def decodeSMBString( flags, text ):
    if flags & smb.SMB.FLAGS2_UNICODE:
        return text.decode('utf-16le')
    else:
        return text

def encodeSMBString( flags, text ):
    if flags & smb.SMB.FLAGS2_UNICODE:
        return (text).encode('utf-16le')
    else:
        return text

def getFileTime(t):
    t *= 10000000
    t += 116444736000000000
    return t

def getUnixTime(t):
    t -= 116444736000000000
    t /= 10000000
    return t

def getSMBDate(t):
    # TODO: Fix this :P
    d = datetime.date.fromtimestamp(t)
    year = d.year - 1980
    ret = (year << 8) + (d.month << 4) + d.day
    return ret

def getSMBTime(t):
    # TODO: Fix this :P
    d = datetime.datetime.fromtimestamp(t)
    return (d.hour << 8) + (d.minute << 4) + d.second

def getShares(connId, smbServer):
    config = smbServer.getServerConfig()
    sections = config.sections()
    # Remove the global one
    del(sections[sections.index('global')])
    shares = {}
    for i in sections:
        shares[i] = dict(config.items(i))
    return shares

def searchShare(connId, share, smbServer):
    config = smbServer.getServerConfig()
    if config.has_section(share):
       return dict(config.items(share))
    else:
       return None

def openFile(path,fileName, accessMode, fileAttributes, openMode):
    fileName = os.path.normpath(fileName.replace('\\','/'))
    errorCode = 0
    if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
       # strip leading '/'
       fileName = fileName[1:]
    pathName = os.path.join(path,fileName)
    mode = 0
    # Check the Open Mode
    if openMode & 0x10:
        # If the file does not exist, create it.
        mode = os.O_CREAT
    else:
        # If file does not exist, return an error
        if os.path.exists(pathName) is not True:
            errorCode = STATUS_NO_SUCH_FILE
            return 0,mode, pathName, errorCode

    if os.path.isdir(pathName) and (fileAttributes & smb.ATTR_DIRECTORY) == 0:
        # Request to open a normal file and this is actually a directory
            errorCode = STATUS_FILE_IS_A_DIRECTORY
            return 0, mode, pathName, errorCode
    # Check the Access Mode
    if accessMode & 0x7 == 1:
       mode |= os.O_WRONLY
    elif accessMode & 0x7 == 2:
       mode |= os.O_RDWR
    else:
       mode = os.O_RDONLY

    try:
        if sys.platform == 'win32':
            mode |= os.O_BINARY
        fid = os.open(pathName, mode)
    except Exception as e:
        LOG.error("openFile: %s,%s" % (pathName, mode) ,e)
        fid = 0
        errorCode = STATUS_ACCESS_DENIED

    return fid, mode, pathName, errorCode

def queryFsInformation(path, filename, level=0):

    if isinstance(filename,unicode):
         encoding = 'utf-16le'
         flags    = smb.SMB.FLAGS2_UNICODE
    else:
         encoding = 'ascii'
         flags    = 0

    fileName = os.path.normpath(filename.replace('\\','/'))
    if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
       # strip leading '/'
       fileName = fileName[1:]
    pathName = os.path.join(path,fileName)
    fileSize = os.path.getsize(pathName)
    (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
    if level == smb.SMB_QUERY_FS_ATTRIBUTE_INFO or level == smb2.SMB2_FILESYSTEM_ATTRIBUTE_INFO:
        data = smb.SMBQueryFsAttributeInfo()
        data['FileSystemAttributes']      = smb.FILE_CASE_SENSITIVE_SEARCH | smb.FILE_CASE_PRESERVED_NAMES
        data['MaxFilenNameLengthInBytes'] = 255
        data['LengthOfFileSystemName']    = len('XTFS')*2
        data['FileSystemName']            = 'XTFS'.encode('utf-16le')
        return data.getData()
    elif level == smb.SMB_INFO_VOLUME:
        data = smb.SMBQueryFsInfoVolume( flags = flags )
        data['VolumeLabel']               = 'SHARE'.encode(encoding)
        return data.getData()
    elif level == smb.SMB_QUERY_FS_VOLUME_INFO or level == smb2.SMB2_FILESYSTEM_VOLUME_INFO:
        data = smb.SMBQueryFsVolumeInfo()
        data['VolumeLabel']               = ''
        data['VolumeCreationTime']        = getFileTime(ctime)
        return data.getData()
    elif level == smb.SMB_QUERY_FS_SIZE_INFO:
        data = smb.SMBQueryFsSizeInfo()
        return data.getData()
    elif level == smb.FILE_FS_FULL_SIZE_INFORMATION:
        data = smb.SMBFileFsFullSizeInformation()
        return data.getData()
    elif level == smb.FILE_FS_SIZE_INFORMATION:
        data = smb.FileFsSizeInformation()
        return data.getData()
    else:
        lastWriteTime = mtime
        attribs = 0
        if os.path.isdir(pathName):
            attribs |= smb.SMB_FILE_ATTRIBUTE_DIRECTORY
        if os.path.isfile(pathName):
            attribs |= smb.SMB_FILE_ATTRIBUTE_NORMAL
        fileAttributes = attribs
        return fileSize, lastWriteTime, fileAttributes

def findFirst2(path, fileName, level, searchAttributes, isSMB2 = False):
     # TODO: Depending on the level, this could be done much simpler

     #print "FindFirs2 path:%s, filename:%s" % (path, fileName)
     fileName = os.path.normpath(fileName.replace('\\','/'))
     # Let's choose the right encoding depending on the request
     if isinstance(fileName,unicode):
         encoding = 'utf-16le'
         flags    = smb.SMB.FLAGS2_UNICODE
     else:
         encoding = 'ascii'
         flags    = 0

     if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
        # strip leading '/'
        fileName = fileName[1:]

     pathName = os.path.join(path,fileName)
     files = []

     if pathName.find('*') == -1 and pathName.find('?') == -1:
         # No search patterns
         pattern = ''
     else:
         pattern = os.path.basename(pathName)
         dirName = os.path.dirname(pathName)

     # Always add . and .. Not that important for Windows, but Samba whines if
     # not present (for * search only)
     if pattern == '*':
         files.append(os.path.join(dirName,'.'))
         files.append(os.path.join(dirName,'..'))

     if pattern != '':
         for file in os.listdir(dirName):
             if fnmatch.fnmatch(file.lower(),pattern.lower()):
                entry = os.path.join(dirName, file)
                if os.path.isdir(entry):
                    if searchAttributes & smb.ATTR_DIRECTORY:
                        files.append(entry)
                else:
                    files.append(entry)
     else:
         if os.path.exists(pathName):
             files.append(pathName)

     searchResult = []
     searchCount = len(files)
     errorCode = STATUS_SUCCESS

     for i in files:
        if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:
            item = smb.SMBFindFileBothDirectoryInfo( flags = flags )
        elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO or level == smb2.SMB2_FILE_DIRECTORY_INFO:
            item = smb.SMBFindFileDirectoryInfo( flags = flags )
        elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
            item = smb.SMBFindFileFullDirectoryInfo( flags = flags )
        elif level == smb.SMB_FIND_INFO_STANDARD:
            item = smb.SMBFindInfoStandard( flags = flags )
        elif level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_FULL_DIRECTORY_INFO:
            item = smb.SMBFindFileIdFullDirectoryInfo( flags = flags )
        elif level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO:
            item = smb.SMBFindFileIdBothDirectoryInfo( flags = flags )
        elif level == smb.SMB_FIND_FILE_NAMES_INFO or level == smb2.SMB2_FILE_NAMES_INFO:
            item = smb.SMBFindFileNamesInfo( flags = flags )
        else:
            LOG.error("Wrong level %d!" % level)
            return  searchResult, searchCount, STATUS_NOT_SUPPORTED

        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(i)
        if os.path.isdir(i):
           item['ExtFileAttributes'] = smb.ATTR_DIRECTORY
        else:
           item['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

        item['FileName'] = os.path.basename(i).encode(encoding)

        if level == smb.SMB_FIND_FILE_BOTH_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_ID_BOTH_DIRECTORY_INFO or level == smb2.SMB2_FILE_BOTH_DIRECTORY_INFO:
           item['EaSize']            = 0
           item['EndOfFile']         = size
           item['AllocationSize']    = size
           item['CreationTime']      = getFileTime(ctime)
           item['LastAccessTime']    = getFileTime(atime)
           item['LastWriteTime']     = getFileTime(mtime)
           item['LastChangeTime']    = getFileTime(mtime)
           item['ShortName']         = '\x00'*24
           item['FileName']          = os.path.basename(i).encode(encoding)
           padLen = (8-(len(item) % 8)) % 8
           item['NextEntryOffset']   = len(item) + padLen
        elif level == smb.SMB_FIND_FILE_DIRECTORY_INFO:
           item['EndOfFile']         = size
           item['AllocationSize']    = size
           item['CreationTime']      = getFileTime(ctime)
           item['LastAccessTime']    = getFileTime(atime)
           item['LastWriteTime']     = getFileTime(mtime)
           item['LastChangeTime']    = getFileTime(mtime)
           item['FileName']          = os.path.basename(i).encode(encoding)
           padLen = (8-(len(item) % 8)) % 8
           item['NextEntryOffset']   = len(item) + padLen
        elif level == smb.SMB_FIND_FILE_FULL_DIRECTORY_INFO or level == smb.SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO or level == smb2.SMB2_FULL_DIRECTORY_INFO:
           item['EaSize']            = 0
           item['EndOfFile']         = size
           item['AllocationSize']    = size
           item['CreationTime']      = getFileTime(ctime)
           item['LastAccessTime']    = getFileTime(atime)
           item['LastWriteTime']     = getFileTime(mtime)
           item['LastChangeTime']    = getFileTime(mtime)
           padLen = (8-(len(item) % 8)) % 8
           item['NextEntryOffset']   = len(item) + padLen
        elif level == smb.SMB_FIND_INFO_STANDARD:
           item['EaSize']            = size
           item['CreationDate']      = getSMBDate(ctime)
           item['CreationTime']      = getSMBTime(ctime)
           item['LastAccessDate']    = getSMBDate(atime)
           item['LastAccessTime']    = getSMBTime(atime)
           item['LastWriteDate']     = getSMBDate(mtime)
           item['LastWriteTime']     = getSMBTime(mtime)
        searchResult.append(item)

     # No more files
     if (level >= smb.SMB_FIND_FILE_DIRECTORY_INFO or isSMB2 == True) and searchCount > 0:
         searchResult[-1]['NextEntryOffset'] = 0

     return searchResult, searchCount, errorCode

def queryFileInformation(path, filename, level):
    #print "queryFileInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)
    return queryPathInformation(path,filename, level)

def queryPathInformation(path, filename, level):
    # TODO: Depending on the level, this could be done much simpler
  #print "queryPathInfo path: %s, filename: %s, level:0x%x" % (path,filename,level)
  try:
    errorCode = 0
    fileName = os.path.normpath(filename.replace('\\','/'))
    if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':
       # strip leading '/'
       fileName = fileName[1:]
    pathName = os.path.join(path,fileName)
    if os.path.exists(pathName):
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
        if level == smb.SMB_QUERY_FILE_BASIC_INFO:
            infoRecord = smb.SMBQueryFileBasicInfo()
            infoRecord['CreationTime']         = getFileTime(ctime)
            infoRecord['LastAccessTime']       = getFileTime(atime)
            infoRecord['LastWriteTime']        = getFileTime(mtime)
            infoRecord['LastChangeTime']       = getFileTime(mtime)
            if os.path.isdir(pathName):
               infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
            else:
               infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
        elif level == smb.SMB_QUERY_FILE_STANDARD_INFO:
            infoRecord = smb.SMBQueryFileStandardInfo()
            infoRecord['AllocationSize']       = size
            infoRecord['EndOfFile']            = size
            if os.path.isdir(pathName):
               infoRecord['Directory']         = 1
            else:
               infoRecord['Directory']         = 0
        elif level == smb.SMB_QUERY_FILE_ALL_INFO or level == smb2.SMB2_FILE_ALL_INFO:
            infoRecord = smb.SMBQueryFileAllInfo()
            infoRecord['CreationTime']         = getFileTime(ctime)
            infoRecord['LastAccessTime']       = getFileTime(atime)
            infoRecord['LastWriteTime']        = getFileTime(mtime)
            infoRecord['LastChangeTime']       = getFileTime(mtime)
            if os.path.isdir(pathName):
               infoRecord['ExtFileAttributes'] = smb.ATTR_DIRECTORY
            else:
               infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
            infoRecord['AllocationSize']       = size
            infoRecord['EndOfFile']            = size
            if os.path.isdir(pathName):
               infoRecord['Directory']         = 1
            else:
               infoRecord['Directory']         = 0
            infoRecord['FileName']             = filename.encode('utf-16le')
        elif level == smb2.SMB2_FILE_NETWORK_OPEN_INFO:
            infoRecord = smb.SMBFileNetworkOpenInfo()
            infoRecord['CreationTime']         = getFileTime(ctime)
            infoRecord['LastAccessTime']       = getFileTime(atime)
            infoRecord['LastWriteTime']        = getFileTime(mtime)
            infoRecord['ChangeTime']           = getFileTime(mtime)
            infoRecord['AllocationSize']       = size
            infoRecord['EndOfFile']            = size
            if os.path.isdir(pathName):
               infoRecord['FileAttributes'] = smb.ATTR_DIRECTORY
            else:
               infoRecord['FileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE
        elif level == smb.SMB_QUERY_FILE_EA_INFO or level == smb2.SMB2_FILE_EA_INFO:
            infoRecord = smb.SMBQueryFileEaInfo()
        elif level == smb2.SMB2_FILE_STREAM_INFO:
            infoRecord = smb.SMBFileStreamInformation()
        else:
            LOG.error('Unknown level for query path info! 0x%x' % level)
            # UNSUPPORTED
            return None, STATUS_NOT_SUPPORTED

        return infoRecord, errorCode
    else:
        # NOT FOUND
        return None, STATUS_OBJECT_NAME_NOT_FOUND
  except Exception as e:
      LOG.error('queryPathInfo: %s' % e)
      raise

def queryDiskInformation(path):
# TODO: Do something useful here :)
# For now we just return fake values
   totalUnits = 65535
   freeUnits = 65535
   return totalUnits, freeUnits

# Here we implement the NT transaction handlers
class NTTRANSCommands:
    def default(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        pass

# Here we implement the NT transaction handlers
class TRANSCommands:
    @staticmethod
    def lanMan(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        # Minimal [MS-RAP] implementation, just to return the shares
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = STATUS_SUCCESS
        if struct.unpack('<H',parameters[:2])[0] == 0:
            # NetShareEnum Request
            netShareEnum = smb.SMBNetShareEnum(parameters)
            if netShareEnum['InfoLevel'] == 1:
                shares = getShares(connId, smbServer)
                respParameters = smb.SMBNetShareEnumResponse()
                respParameters['EntriesReturned']  = len(shares)
                respParameters['EntriesAvailable'] = len(shares)
                tailData = ''
                for i in shares:
                    # NetShareInfo1 len == 20
                    entry = smb.NetShareInfo1()
                    entry['NetworkName'] = i + '\x00'*(13-len(i))
                    entry['Type']        = int(shares[i]['share type'])
                    # (beto) If offset == 0 it crashes explorer.exe on windows 7
                    entry['RemarkOffsetLow'] = 20 * len(shares) + len(tailData)
                    respData += entry.getData()
                    if 'comment' in shares[i]:
                        tailData += shares[i]['comment'] + '\x00'
                    else:
                        tailData += '\x00'
                respData += tailData
            else:
                # We don't support other info levels
                errorCode = STATUS_NOT_SUPPORTED
        elif struct.unpack('<H',parameters[:2])[0] == 13:
            # NetrServerGetInfo Request
            respParameters = smb.SMBNetServerGetInfoResponse()
            netServerInfo = smb.SMBNetServerInfo1()
            netServerInfo['ServerName'] = smbServer.getServerName()
            respData = str(netServerInfo)
            respParameters['TotalBytesAvailable'] = len(respData)
        elif struct.unpack('<H',parameters[:2])[0] == 1:
            # NetrShareGetInfo Request
            request = smb.SMBNetShareGetInfo(parameters)
            respParameters = smb.SMBNetShareGetInfoResponse()
            shares = getShares(connId, smbServer)
            share = shares[request['ShareName'].upper()]
            shareInfo = smb.NetShareInfo1()
            shareInfo['NetworkName'] = request['ShareName'].upper() + '\x00'
            shareInfo['Type']        = int(share['share type'])
            respData = shareInfo.getData()
            if 'comment' in share:
                shareInfo['RemarkOffsetLow'] = len(respData)
                respData += share['comment'] + '\x00'
            respParameters['TotalBytesAvailable'] = len(respData)

        else:
            # We don't know how to handle anything else
            errorCode = STATUS_NOT_SUPPORTED

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    @staticmethod
    def transactNamedPipe(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = STATUS_SUCCESS
        SMBCommand  = smb.SMBCommand(recvPacket['Data'][0])
        transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters'])

        # Extract the FID
        fid = struct.unpack('<H', transParameters['Setup'][2:])[0]

        if fid in connData['OpenedFiles']:
            fileHandle = connData['OpenedFiles'][fid]['FileHandle']
            if fileHandle != PIPE_FILE_DESCRIPTOR:
                os.write(fileHandle,data)
                respData = os.read(fileHandle,data)
            else:
                sock = connData['OpenedFiles'][fid]['Socket']
                sock.send(data)
                respData = sock.recv(maxDataCount)
        else:
            errorCode = STATUS_INVALID_HANDLE

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

# Here we implement the transaction2 handlers
class TRANS2Commands:
    # All these commands return setup, parameters, data, errorCode
    @staticmethod
    def setPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = STATUS_SUCCESS
        setPathInfoParameters = smb.SMBSetPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)
        if recvPacket['Tid'] in connData['ConnectedShares']:
            path     = connData['ConnectedShares'][recvPacket['Tid']]['path']
            fileName = decodeSMBString(recvPacket['Flags2'], setPathInfoParameters['FileName'])
            fileName = os.path.normpath(fileName.replace('\\','/'))
            if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\') and path != '':
               # strip leading '/'
               fileName = fileName[1:]
            pathName = os.path.join(path,fileName)
            if os.path.exists(pathName):
                informationLevel = setPathInfoParameters['InformationLevel']
                if informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
                    infoRecord = smb.SMBSetFileBasicInfo(data)
                    # Creation time won't be set,  the other ones we play with.
                    atime = infoRecord['LastAccessTime']
                    if atime == 0:
                        atime = -1
                    else:
                        atime = getUnixTime(atime)
                    mtime = infoRecord['LastWriteTime']
                    if mtime == 0:
                        mtime = -1
                    else:
                        mtime = getUnixTime(mtime)
                    if mtime != -1 or atime != -1:
                        os.utime(pathName,(atime,mtime))
                else:
                    smbServer.log('Unknown level for set path info! 0x%x' % setPathInfoParameters['InformationLevel'], logging.ERROR)
                    # UNSUPPORTED
                    errorCode =  STATUS_NOT_SUPPORTED
            else:
                errorCode = STATUS_OBJECT_NAME_NOT_FOUND

            if errorCode == STATUS_SUCCESS:
                respParameters = smb.SMBSetPathInformationResponse_Parameters()

        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode


    @staticmethod
    def setFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = STATUS_SUCCESS
        setFileInfoParameters = smb.SMBSetFileInformation_Parameters(parameters)

        if recvPacket['Tid'] in connData['ConnectedShares']:
            if setFileInfoParameters['FID'] in connData['OpenedFiles']:
                fileName = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileName']
                informationLevel = setFileInfoParameters['InformationLevel']
                if informationLevel == smb.SMB_SET_FILE_DISPOSITION_INFO:
                    infoRecord = smb.SMBSetFileDispositionInfo(parameters)
                    if infoRecord['DeletePending'] > 0:
                       # Mark this file for removal after closed
                       connData['OpenedFiles'][setFileInfoParameters['FID']]['DeleteOnClose'] = True
                       respParameters = smb.SMBSetFileInformationResponse_Parameters()
                elif informationLevel == smb.SMB_SET_FILE_BASIC_INFO:
                    infoRecord = smb.SMBSetFileBasicInfo(data)
                    # Creation time won't be set,  the other ones we play with.
                    atime = infoRecord['LastAccessTime']
                    if atime == 0:
                        atime = -1
                    else:
                        atime = getUnixTime(atime)
                    mtime = infoRecord['LastWriteTime']
                    if mtime == 0:
                        mtime = -1
                    else:
                        mtime = getUnixTime(mtime)
                    os.utime(fileName,(atime,mtime))
                elif informationLevel == smb.SMB_SET_FILE_END_OF_FILE_INFO:
                    fileHandle = connData['OpenedFiles'][setFileInfoParameters['FID']]['FileHandle']
                    infoRecord = smb.SMBSetFileEndOfFileInfo(data)
                    if infoRecord['EndOfFile'] > 0:
                        os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0)
                        os.write(fileHandle, '\x00')
                else:
                    smbServer.log('Unknown level for set file info! 0x%x' % setFileInfoParameters['InformationLevel'], logging.ERROR)
                    # UNSUPPORTED
                    errorCode =  STATUS_NOT_SUPPORTED
            else:
                errorCode = STATUS_NO_SUCH_FILE

            if errorCode == STATUS_SUCCESS:
                respParameters = smb.SMBSetFileInformationResponse_Parameters()
        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    @staticmethod
    def queryFileInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''

        queryFileInfoParameters = smb.SMBQueryFileInformation_Parameters(parameters)

        if recvPacket['Tid'] in connData['ConnectedShares']:
            if queryFileInfoParameters['FID'] in connData['OpenedFiles']:
                fileName = connData['OpenedFiles'][queryFileInfoParameters['FID']]['FileName']

                infoRecord, errorCode = queryFileInformation('', fileName, queryFileInfoParameters['InformationLevel'])

                if infoRecord is not None:
                    respParameters = smb.SMBQueryFileInformationResponse_Parameters()
                    respData = infoRecord
            else:
                errorCode = STATUS_INVALID_HANDLE
        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    @staticmethod
    def queryPathInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = 0

        queryPathInfoParameters = smb.SMBQueryPathInformation_Parameters(flags = recvPacket['Flags2'], data = parameters)

        if recvPacket['Tid'] in connData['ConnectedShares']:
            path = connData['ConnectedShares'][recvPacket['Tid']]['path']
            try:
               infoRecord, errorCode = queryPathInformation(path, decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName']), queryPathInfoParameters['InformationLevel'])
            except Exception as e:
               smbServer.log("queryPathInformation: %s" % e,logging.ERROR)

            if infoRecord is not None:
                respParameters = smb.SMBQueryPathInformationResponse_Parameters()
                respData = infoRecord
        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    @staticmethod
    def queryFsInformation(connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        connData = smbServer.getConnectionData(connId)
        errorCode = 0
        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
            data = queryFsInformation(connData['ConnectedShares'][recvPacket['Tid']]['path'], '', struct.unpack('<H',parameters)[0])

        smbServer.setConnectionData(connId, connData)

        return '','', data, errorCode

    @staticmethod
    def findNext2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        errorCode = STATUS_SUCCESS
        findNext2Parameters = smb.SMBFindNext2_Parameters(flags = recvPacket['Flags2'], data = parameters)

        sid = findNext2Parameters['SID']
        if recvPacket['Tid'] in connData['ConnectedShares']:
            if sid in connData['SIDs']:
                searchResult = connData['SIDs'][sid]
                respParameters = smb.SMBFindNext2Response_Parameters()
                endOfSearch = 1
                searchCount = 1
                totalData = 0
                for i in enumerate(searchResult):
                    data = i[1].getData()
                    lenData = len(data)
                    if (totalData+lenData) >= maxDataCount or (i[0]+1) >= findNext2Parameters['SearchCount']:
                        # We gotta stop here and continue on a find_next2
                        endOfSearch = 0
                        connData['SIDs'][sid] = searchResult[i[0]:]
                        respParameters['LastNameOffset'] = totalData
                        break
                    else:
                        searchCount +=1
                        respData += data
                        totalData += lenData

                # Have we reached the end of the search or still stuff to send?
                if endOfSearch > 0:
                    # Let's remove the SID from our ConnData
                    del(connData['SIDs'][sid])

                respParameters['EndOfSearch'] = endOfSearch
                respParameters['SearchCount'] = searchCount
            else:
                errorCode = STATUS_INVALID_HANDLE
        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    @staticmethod
    def findFirst2(connId, smbServer, recvPacket, parameters, data, maxDataCount):
        connData = smbServer.getConnectionData(connId)

        respSetup = ''
        respParameters = ''
        respData = ''
        findFirst2Parameters = smb.SMBFindFirst2_Parameters( recvPacket['Flags2'], data = parameters)

        if recvPacket['Tid'] in connData['ConnectedShares']:
            path = connData['ConnectedShares'][recvPacket['Tid']]['path']

            searchResult, searchCount, errorCode = findFirst2(path,
                          decodeSMBString( recvPacket['Flags2'], findFirst2Parameters['FileName'] ),
                          findFirst2Parameters['InformationLevel'],
                          findFirst2Parameters['SearchAttributes'] )

            respParameters = smb.SMBFindFirst2Response_Parameters()
            endOfSearch = 1
            sid = 0x80 # default SID
            searchCount = 0
            totalData = 0
            for i in enumerate(searchResult):
                #i[1].dump()
                data = i[1].getData()
                lenData = len(data)
                if (totalData+lenData) >= maxDataCount or (i[0]+1) > findFirst2Parameters['SearchCount']:
                    # We gotta stop here and continue on a find_next2
                    endOfSearch = 0
                    # Simple way to generate a fid
                    if len(connData['SIDs']) == 0:
                       sid = 1
                    else:
                       sid = connData['SIDs'].keys()[-1] + 1
                    # Store the remaining search results in the ConnData SID
                    connData['SIDs'][sid] = searchResult[i[0]:]
                    respParameters['LastNameOffset'] = totalData
                    break
                else:
                    searchCount +=1
                    respData += data

                    padLen = (8-(lenData % 8)) %8
                    respData += '\xaa'*padLen
                    totalData += lenData + padLen

            respParameters['SID'] = sid
            respParameters['EndOfSearch'] = endOfSearch
            respParameters['SearchCount'] = searchCount
        else:
            errorCode = STATUS_SMB_BAD_TID

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

# Here we implement the commands handlers
class SMBCommands:

    @staticmethod
    def smbTransaction(connId, smbServer, SMBCommand, recvPacket, transCommands):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(recvPacket['Command'])

        transParameters= smb.SMBTransaction_Parameters(SMBCommand['Parameters'])

        # Do the stuff
        if transParameters['ParameterCount'] != transParameters['TotalParameterCount']:
            # TODO: Handle partial parameters
            raise Exception("Unsupported partial parameters in TRANSACT2!")
        else:
            transData = smb.SMBTransaction_SData(flags = recvPacket['Flags2'])
            # Standard says servers shouldn't trust Parameters and Data comes
            # in order, so we have to parse the offsets, ugly

            paramCount = transParameters['ParameterCount']
            transData['Trans_ParametersLength'] = paramCount
            dataCount = transParameters['DataCount']
            transData['Trans_DataLength'] = dataCount
            transData.fromString(SMBCommand['Data'])
            if transParameters['ParameterOffset'] > 0:
                paramOffset = transParameters['ParameterOffset'] - 63 - transParameters['SetupLength']
                transData['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount]
            else:
                transData['Trans_Parameters'] = ''

            if transParameters['DataOffset'] > 0:
                dataOffset = transParameters['DataOffset'] - 63 - transParameters['SetupLength']
                transData['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
            else:
                transData['Trans_Data'] = ''

            # Call the handler for this TRANSACTION
            if transParameters['SetupCount'] == 0:
                # No subcommand, let's play with the Name
                command = decodeSMBString(recvPacket['Flags2'],transData['Name'])
            else:
                command = struct.unpack('<H', transParameters['Setup'][:2])[0]

            if command in transCommands:
               # Call the TRANS subcommand
               setup = ''
               parameters = ''
               data = ''
               try:
                   setup, parameters, data, errorCode = transCommands[command](connId,
                                smbServer,
                                recvPacket,
                                transData['Trans_Parameters'],
                                transData['Trans_Data'],
                                transParameters['MaxDataCount'])
               except Exception as e:
                   #print 'Transaction: %s' % e,e
                   smbServer.log('Transaction: (%r,%s)' % (command, e), logging.ERROR)
                   errorCode = STATUS_ACCESS_DENIED
                   #raise

               if setup == '' and parameters == '' and data == '':
                   # Something wen't wrong
                   respParameters = ''
                   respData = ''
               else:
                   # Build the answer
                   data = str(data)
                   remainingData = len(data)
                   parameters = str(parameters)
                   remainingParameters = len(parameters)
                   commands = []
                   dataDisplacement = 0
                   while remainingData > 0 or remainingParameters > 0:
                       respSMBCommand = smb.SMBCommand(recvPacket['Command'])
                       respParameters = smb.SMBTransactionResponse_Parameters()
                       respData       = smb.SMBTransaction2Response_Data()

                       respParameters['TotalParameterCount'] = len(parameters)
                       respParameters['ParameterCount']      = len(parameters)
                       respData['Trans_ParametersLength']    = len(parameters)
                       respParameters['TotalDataCount']      = len(data)
                       respParameters['DataDisplacement']    = dataDisplacement

                       # TODO: Do the same for parameters
                       if len(data) >  transParameters['MaxDataCount']:
                           # Answer doesn't fit in this packet
                           LOG.debug("Lowering answer from %d to %d" % (len(data),transParameters['MaxDataCount']) )
                           respParameters['DataCount'] = transParameters['MaxDataCount']
                       else:
                           respParameters['DataCount'] = len(data)

                       respData['Trans_DataLength']          = respParameters['DataCount']
                       respParameters['SetupCount']          = len(setup)
                       respParameters['Setup']               = setup
                       # TODO: Make sure we're calculating the pad right
                       if len(parameters) > 0:
                           #padLen = 4 - (55 + len(setup)) % 4
                           padLen = (4 - (55 + len(setup)) % 4 ) % 4
                           padBytes = '\xFF' * padLen
                           respData['Pad1'] = padBytes
                           respParameters['ParameterOffset'] = 55 + len(setup) + padLen
                       else:
                           padLen = 0
                           respParameters['ParameterOffset'] = 0
                           respData['Pad1']                  = ''

                       if len(data) > 0:
                           #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
                           pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
                           respData['Pad2'] = '\xFF' * pad2Len
                           respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
                       else:
                           respParameters['DataOffset'] = 0
                           respData['Pad2']             = ''

                       respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
                       respData['Trans_Data']       = data[:respParameters['DataCount']]
                       respSMBCommand['Parameters'] = respParameters
                       respSMBCommand['Data']       = respData

                       data = data[respParameters['DataCount']:]
                       remainingData -= respParameters['DataCount']
                       dataDisplacement += respParameters['DataCount'] + 1

                       parameters = parameters[respParameters['ParameterCount']:]
                       remainingParameters -= respParameters['ParameterCount']
                       commands.append(respSMBCommand)

                   smbServer.setConnectionData(connId, connData)
                   return commands, None, errorCode

            else:
               smbServer.log("Unsupported Transact command %r" % command, logging.ERROR)
               respParameters = ''
               respData = ''
               errorCode = STATUS_NOT_IMPLEMENTED

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbNTTransact(connId, smbServer, SMBCommand, recvPacket, transCommands):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(recvPacket['Command'])

        NTTransParameters= smb.SMBNTTransaction_Parameters(SMBCommand['Parameters'])
        # Do the stuff
        if NTTransParameters['ParameterCount'] != NTTransParameters['TotalParameterCount']:
            # TODO: Handle partial parameters
            raise Exception("Unsupported partial parameters in NTTrans!")
        else:
            NTTransData = smb.SMBNTTransaction_Data()
            # Standard says servers shouldn't trust Parameters and Data comes
            # in order, so we have to parse the offsets, ugly

            paramCount = NTTransParameters['ParameterCount']
            NTTransData['NT_Trans_ParametersLength'] = paramCount
            dataCount = NTTransParameters['DataCount']
            NTTransData['NT_Trans_DataLength'] = dataCount

            if NTTransParameters['ParameterOffset'] > 0:
                paramOffset = NTTransParameters['ParameterOffset'] - 73 - NTTransParameters['SetupLength']
                NTTransData['NT_Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount]
            else:
                NTTransData['NT_Trans_Parameters'] = ''

            if NTTransParameters['DataOffset'] > 0:
                dataOffset = NTTransParameters['DataOffset'] - 73 - NTTransParameters['SetupLength']
                NTTransData['NT_Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
            else:
                NTTransData['NT_Trans_Data'] = ''

            # Call the handler for this TRANSACTION
            command = NTTransParameters['Function']
            if command in transCommands:
               # Call the NT TRANS subcommand
               setup = ''
               parameters = ''
               data = ''
               try:
                   setup, parameters, data, errorCode = transCommands[command](connId,
                                smbServer,
                                recvPacket,
                                NTTransData['NT_Trans_Parameters'],
                                NTTransData['NT_Trans_Data'],
                                NTTransParameters['MaxDataCount'])
               except Exception as e:
                   smbServer.log('NTTransaction: (0x%x,%s)' % (command, e), logging.ERROR)
                   errorCode = STATUS_ACCESS_DENIED
                   #raise

               if setup == '' and parameters == '' and data == '':
                   # Something wen't wrong
                   respParameters = ''
                   respData = ''
                   if errorCode == STATUS_SUCCESS:
                       errorCode = STATUS_ACCESS_DENIED
               else:
                   # Build the answer
                   data = str(data)
                   remainingData = len(data)
                   parameters = str(parameters)
                   remainingParameters = len(parameters)
                   commands = []
                   dataDisplacement = 0
                   while remainingData > 0 or remainingParameters > 0:
                       respSMBCommand = smb.SMBCommand(recvPacket['Command'])
                       respParameters = smb.SMBNTTransactionResponse_Parameters()
                       respData       = smb.SMBNTTransactionResponse_Data()

                       respParameters['TotalParameterCount'] = len(parameters)
                       respParameters['ParameterCount']      = len(parameters)
                       respData['Trans_ParametersLength']    = len(parameters)
                       respParameters['TotalDataCount']      = len(data)
                       respParameters['DataDisplacement']    = dataDisplacement
                       # TODO: Do the same for parameters
                       if len(data) >  NTTransParameters['MaxDataCount']:
                           # Answer doesn't fit in this packet
                           LOG.debug("Lowering answer from %d to %d" % (len(data),NTTransParameters['MaxDataCount']) )
                           respParameters['DataCount'] = NTTransParameters['MaxDataCount']
                       else:
                           respParameters['DataCount'] = len(data)

                       respData['NT_Trans_DataLength']          = respParameters['DataCount']
                       respParameters['SetupCount']          = len(setup)
                       respParameters['Setup']               = setup
                       # TODO: Make sure we're calculating the pad right
                       if len(parameters) > 0:
                           #padLen = 4 - (71 + len(setup)) % 4
                           padLen = (4 - (73 + len(setup)) % 4 ) % 4
                           padBytes = '\xFF' * padLen
                           respData['Pad1'] = padBytes
                           respParameters['ParameterOffset'] = 73 + len(setup) + padLen
                       else:
                           padLen = 0
                           respParameters['ParameterOffset'] = 0
                           respData['Pad1']                  = ''

                       if len(data) > 0:
                           #pad2Len = 4 - (71 + len(setup) + padLen + len(parameters)) % 4
                           pad2Len = (4 - (73 + len(setup) + padLen + len(parameters)) % 4) % 4
                           respData['Pad2'] = '\xFF' * pad2Len
                           respParameters['DataOffset'] = 73 + len(setup) + padLen + len(parameters) + pad2Len
                       else:
                           respParameters['DataOffset'] = 0
                           respData['Pad2']             = ''

                       respData['NT_Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
                       respData['NT_Trans_Data']       = data[:respParameters['DataCount']]
                       respSMBCommand['Parameters'] = respParameters
                       respSMBCommand['Data']       = respData

                       data = data[respParameters['DataCount']:]
                       remainingData -= respParameters['DataCount']
                       dataDisplacement += respParameters['DataCount'] + 1

                       parameters = parameters[respParameters['ParameterCount']:]
                       remainingParameters -= respParameters['ParameterCount']
                       commands.append(respSMBCommand)

                   smbServer.setConnectionData(connId, connData)
                   return commands, None, errorCode

            else:
               #smbServer.log("Unsupported NTTransact command 0x%x" % command, logging.ERROR)
               respParameters = ''
               respData = ''
               errorCode = STATUS_NOT_IMPLEMENTED

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbTransaction2(connId, smbServer, SMBCommand, recvPacket, transCommands):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(recvPacket['Command'])

        trans2Parameters= smb.SMBTransaction2_Parameters(SMBCommand['Parameters'])

        # Do the stuff
        if trans2Parameters['ParameterCount'] != trans2Parameters['TotalParameterCount']:
            # TODO: Handle partial parameters
            #print "Unsupported partial parameters in TRANSACT2!"
            raise Exception("Unsupported partial parameters in TRANSACT2!")
        else:
            trans2Data = smb.SMBTransaction2_Data()
            # Standard says servers shouldn't trust Parameters and Data comes
            # in order, so we have to parse the offsets, ugly

            paramCount = trans2Parameters['ParameterCount']
            trans2Data['Trans_ParametersLength'] = paramCount
            dataCount = trans2Parameters['DataCount']
            trans2Data['Trans_DataLength'] = dataCount

            if trans2Parameters['ParameterOffset'] > 0:
                paramOffset = trans2Parameters['ParameterOffset'] - 63 - trans2Parameters['SetupLength']
                trans2Data['Trans_Parameters'] = SMBCommand['Data'][paramOffset:paramOffset+paramCount]
            else:
                trans2Data['Trans_Parameters'] = ''

            if trans2Parameters['DataOffset'] > 0:
                dataOffset = trans2Parameters['DataOffset'] - 63 - trans2Parameters['SetupLength']
                trans2Data['Trans_Data'] = SMBCommand['Data'][dataOffset:dataOffset + dataCount]
            else:
                trans2Data['Trans_Data'] = ''

            # Call the handler for this TRANSACTION
            command = struct.unpack('<H', trans2Parameters['Setup'])[0]
            if command in transCommands:
               # Call the TRANS2 subcommand
               try:
                   setup, parameters, data, errorCode = transCommands[command](connId,
                                smbServer,
                                recvPacket,
                                trans2Data['Trans_Parameters'],
                                trans2Data['Trans_Data'],
                                trans2Parameters['MaxDataCount'])
               except Exception as e:
                   smbServer.log('Transaction2: (0x%x,%s)' % (command, e), logging.ERROR)
                   #import traceback
                   #traceback.print_exc()
                   raise

               if setup == '' and parameters == '' and data == '':
                   # Something wen't wrong
                   respParameters = ''
                   respData = ''
               else:
                   # Build the answer
                   data = str(data)
                   remainingData = len(data)
                   parameters = str(parameters)
                   remainingParameters = len(parameters)
                   commands = []
                   dataDisplacement = 0
                   while remainingData > 0 or remainingParameters > 0:
                       respSMBCommand = smb.SMBCommand(recvPacket['Command'])
                       respParameters = smb.SMBTransaction2Response_Parameters()
                       respData       = smb.SMBTransaction2Response_Data()

                       respParameters['TotalParameterCount'] = len(parameters)
                       respParameters['ParameterCount']      = len(parameters)
                       respData['Trans_ParametersLength']    = len(parameters)
                       respParameters['TotalDataCount']      = len(data)
                       respParameters['DataDisplacement']    = dataDisplacement
                       # TODO: Do the same for parameters
                       if len(data) >  trans2Parameters['MaxDataCount']:
                           # Answer doesn't fit in this packet
                           LOG.debug("Lowering answer from %d to %d" % (len(data),trans2Parameters['MaxDataCount']) )
                           respParameters['DataCount'] = trans2Parameters['MaxDataCount']
                       else:
                           respParameters['DataCount'] = len(data)

                       respData['Trans_DataLength']          = respParameters['DataCount']
                       respParameters['SetupCount']          = len(setup)
                       respParameters['Setup']               = setup
                       # TODO: Make sure we're calculating the pad right
                       if len(parameters) > 0:
                           #padLen = 4 - (55 + len(setup)) % 4
                           padLen = (4 - (55 + len(setup)) % 4 ) % 4
                           padBytes = '\xFF' * padLen
                           respData['Pad1'] = padBytes
                           respParameters['ParameterOffset'] = 55 + len(setup) + padLen
                       else:
                           padLen = 0
                           respParameters['ParameterOffset'] = 0
                           respData['Pad1']                  = ''

                       if len(data) > 0:
                           #pad2Len = 4 - (55 + len(setup) + padLen + len(parameters)) % 4
                           pad2Len = (4 - (55 + len(setup) + padLen + len(parameters)) % 4) % 4
                           respData['Pad2'] = '\xFF' * pad2Len
                           respParameters['DataOffset'] = 55 + len(setup) + padLen + len(parameters) + pad2Len
                       else:
                           respParameters['DataOffset'] = 0
                           respData['Pad2']             = ''

                       respData['Trans_Parameters'] = parameters[:respParameters['ParameterCount']]
                       respData['Trans_Data']       = data[:respParameters['DataCount']]
                       respSMBCommand['Parameters'] = respParameters
                       respSMBCommand['Data']       = respData

                       data = data[respParameters['DataCount']:]
                       remainingData -= respParameters['DataCount']
                       dataDisplacement += respParameters['DataCount'] + 1

                       parameters = parameters[respParameters['ParameterCount']:]
                       remainingParameters -= respParameters['ParameterCount']
                       commands.append(respSMBCommand)

                   smbServer.setConnectionData(connId, connData)
                   return commands, None, errorCode

            else:
               smbServer.log("Unsupported Transact/2 command 0x%x" % command, logging.ERROR)
               respParameters = ''
               respData = ''
               errorCode = STATUS_NOT_IMPLEMENTED

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComLockingAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_LOCKING_ANDX)
        respParameters        = ''
        respData              = ''

        # I'm actually doing nothing.. just make MacOS happy ;)
        errorCode = STATUS_SUCCESS

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbComClose(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_CLOSE)
        respParameters        = ''
        respData              = ''

        comClose =  smb.SMBClose_Parameters(SMBCommand['Parameters'])

        if comClose['FID'] in connData['OpenedFiles']:
             errorCode = STATUS_SUCCESS
             fileHandle = connData['OpenedFiles'][comClose['FID']]['FileHandle']
             try:
                 if fileHandle == PIPE_FILE_DESCRIPTOR:
                     connData['OpenedFiles'][comClose['FID']]['Socket'].close()
                 elif fileHandle != VOID_FILE_DESCRIPTOR:
                     os.close(fileHandle)
             except Exception as e:
                 smbServer.log("comClose %s" % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
             else:
                 # Check if the file was marked for removal
                 if connData['OpenedFiles'][comClose['FID']]['DeleteOnClose'] is True:
                     try:
                         os.remove(connData['OpenedFiles'][comClose['FID']]['FileName'])
                     except Exception as e:
                         smbServer.log("comClose %s" % e, logging.ERROR)
                         errorCode = STATUS_ACCESS_DENIED
                 del(connData['OpenedFiles'][comClose['FID']])
        else:
            errorCode = STATUS_INVALID_HANDLE

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComWrite(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_WRITE)
        respParameters        = smb.SMBWriteResponse_Parameters()
        respData              = ''

        comWriteParameters =  smb.SMBWrite_Parameters(SMBCommand['Parameters'])
        comWriteData = smb.SMBWrite_Data(SMBCommand['Data'])

        if comWriteParameters['Fid'] in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][comWriteParameters['Fid']]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     # TODO: Handle big size files
                     # If we're trying to write past the file end we just skip the write call (Vista does this)
                     if os.lseek(fileHandle, 0, 2) >= comWriteParameters['Offset']:
                         os.lseek(fileHandle,comWriteParameters['Offset'],0)
                         os.write(fileHandle,comWriteData['Data'])
                 else:
                     sock = connData['OpenedFiles'][comWriteParameters['Fid']]['Socket']
                     sock.send(comWriteData['Data'])
                 respParameters['Count']    = comWriteParameters['Count']
             except Exception as e:
                 smbServer.log('smbComWrite: %s' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE


        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComFlush(connId, smbServer, SMBCommand,recvPacket ):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_FLUSH)
        respParameters        = ''
        respData              = ''

        comFlush =  smb.SMBFlush_Parameters(SMBCommand['Parameters'])

        if comFlush['FID'] in connData['OpenedFiles']:
             errorCode = STATUS_SUCCESS
             fileHandle = connData['OpenedFiles'][comFlush['FID']]['FileHandle']
             try:
                 os.fsync(fileHandle)
             except Exception as e:
                 smbServer.log("comFlush %s" % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbComCreateDirectory(connId, smbServer, SMBCommand,recvPacket ):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
        respParameters        = ''
        respData              = ''

        comCreateDirectoryData=  smb.SMBCreateDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             errorCode = STATUS_SUCCESS
             path = connData['ConnectedShares'][recvPacket['Tid']]['path']
             fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comCreateDirectoryData['DirectoryName']).replace('\\','/'))
             if len(fileName) > 0:
                if fileName[0] == '/' or fileName[0] == '\\':
                    # strip leading '/'
                    fileName = fileName[1:]
             pathName = os.path.join(path,fileName)
             if os.path.exists(pathName):
                errorCode = STATUS_OBJECT_NAME_COLLISION

             # TODO: More checks here in the future.. Specially when we support
             # user access
             else:
                 try:
                     os.mkdir(pathName)
                 except Exception as e:
                     smbServer.log("smbComCreateDirectory: %s" % e, logging.ERROR)
                     errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID


        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComRename(connId, smbServer, SMBCommand, recvPacket ):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_RENAME)
        respParameters        = ''
        respData              = ''

        comRenameData      =  smb.SMBRename_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])
        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             errorCode = STATUS_SUCCESS
             path = connData['ConnectedShares'][recvPacket['Tid']]['path']
             oldFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['OldFileName']).replace('\\','/'))
             newFileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comRenameData['NewFileName']).replace('\\','/'))
             if len(oldFileName) > 0 and (oldFileName[0] == '/' or oldFileName[0] == '\\'):
                # strip leading '/'
                oldFileName = oldFileName[1:]
             oldPathName = os.path.join(path,oldFileName)
             if len(newFileName) > 0 and (newFileName[0] == '/' or newFileName[0] == '\\'):
                # strip leading '/'
                newFileName = newFileName[1:]
             newPathName = os.path.join(path,newFileName)

             if os.path.exists(oldPathName) is not True:
                errorCode = STATUS_NO_SUCH_FILE

             # TODO: More checks here in the future.. Specially when we support
             # user access
             else:
                 try:
                     os.rename(oldPathName,newPathName)
                 except OSError as e:
                     smbServer.log("smbComRename: %s" % e, logging.ERROR)
                     errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID


        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComDelete(connId, smbServer, SMBCommand, recvPacket ):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_DELETE)
        respParameters        = ''
        respData              = ''

        comDeleteData         =  smb.SMBDelete_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             errorCode = STATUS_SUCCESS
             path = connData['ConnectedShares'][recvPacket['Tid']]['path']
             fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteData['FileName']).replace('\\','/'))
             if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
                # strip leading '/'
                fileName = fileName[1:]
             pathName = os.path.join(path,fileName)
             if os.path.exists(pathName) is not True:
                errorCode = STATUS_NO_SUCH_FILE

             # TODO: More checks here in the future.. Specially when we support
             # user access
             else:
                 try:
                     os.remove(pathName)
                 except OSError as e:
                     smbServer.log("smbComDelete: %s" % e, logging.ERROR)
                     errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbComDeleteDirectory(connId, smbServer, SMBCommand, recvPacket ):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
        respParameters        = ''
        respData              = ''

        comDeleteDirectoryData=  smb.SMBDeleteDirectory_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             errorCode = STATUS_SUCCESS
             path = connData['ConnectedShares'][recvPacket['Tid']]['path']
             fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],comDeleteDirectoryData['DirectoryName']).replace('\\','/'))
             if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
                # strip leading '/'
                fileName = fileName[1:]
             pathName = os.path.join(path,fileName)
             if os.path.exists(pathName) is not True:
                errorCode = STATUS_NO_SUCH_FILE

             # TODO: More checks here in the future.. Specially when we support
             # user access
             else:
                 try:
                     os.rmdir(pathName)
                 except OSError as e:
                     smbServer.log("smbComDeleteDirectory: %s" % e,logging.ERROR)
                     if e.errno == errno.ENOTEMPTY:
                         errorCode = STATUS_DIRECTORY_NOT_EMPTY
                     else:
                         errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode


    @staticmethod
    def smbComWriteAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_WRITE_ANDX)
        respParameters        = smb.SMBWriteAndXResponse_Parameters()
        respData              = ''

        if SMBCommand['WordCount'] == 0x0C:
            writeAndX =  smb.SMBWriteAndX_Parameters_Short(SMBCommand['Parameters'])
            writeAndXData = smb.SMBWriteAndX_Data_Short()
        else:
            writeAndX =  smb.SMBWriteAndX_Parameters(SMBCommand['Parameters'])
            writeAndXData = smb.SMBWriteAndX_Data()
        writeAndXData['DataLength'] = writeAndX['DataLength']
        writeAndXData['DataOffset'] = writeAndX['DataOffset']
        writeAndXData.fromString(SMBCommand['Data'])


        if writeAndX['Fid'] in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][writeAndX['Fid']]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     offset = writeAndX['Offset']
                     if 'HighOffset' in writeAndX.fields:
                         offset += (writeAndX['HighOffset'] << 32)
                     # If we're trying to write past the file end we just skip the write call (Vista does this)
                     if os.lseek(fileHandle, 0, 2) >= offset:
                         os.lseek(fileHandle,offset,0)
                         os.write(fileHandle,writeAndXData['Data'])
                 else:
                     sock = connData['OpenedFiles'][writeAndX['Fid']]['Socket']
                     sock.send(writeAndXData['Data'])

                 respParameters['Count']    = writeAndX['DataLength']
                 respParameters['Available']= 0xff
             except Exception as e:
                 smbServer.log('smbComWriteAndx: %s' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComRead(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_READ)
        respParameters        = smb.SMBReadResponse_Parameters()
        respData              = smb.SMBReadResponse_Data()

        comReadParameters =  smb.SMBRead_Parameters(SMBCommand['Parameters'])

        if comReadParameters['Fid'] in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][comReadParameters['Fid']]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     # TODO: Handle big size files
                     os.lseek(fileHandle,comReadParameters['Offset'],0)
                     content = os.read(fileHandle,comReadParameters['Count'])
                 else:
                     sock = connData['OpenedFiles'][comReadParameters['Fid']]['Socket']
                     content = sock.recv(comReadParameters['Count'])
                 respParameters['Count']    = len(content)
                 respData['DataLength']     = len(content)
                 respData['Data']           = content
             except Exception as e:
                 smbServer.log('smbComRead: %s ' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComReadAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_READ_ANDX)
        respParameters        = smb.SMBReadAndXResponse_Parameters()
        respData              = ''

        if SMBCommand['WordCount'] == 0x0A:
            readAndX =  smb.SMBReadAndX_Parameters2(SMBCommand['Parameters'])
        else:
            readAndX =  smb.SMBReadAndX_Parameters(SMBCommand['Parameters'])

        if readAndX['Fid'] in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][readAndX['Fid']]['FileHandle']
             errorCode = 0
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     offset = readAndX['Offset']
                     if 'HighOffset' in readAndX.fields:
                         offset += (readAndX['HighOffset'] << 32)
                     os.lseek(fileHandle,offset,0)
                     content = os.read(fileHandle,readAndX['MaxCount'])
                 else:
                     sock = connData['OpenedFiles'][readAndX['Fid']]['Socket']
                     content = sock.recv(readAndX['MaxCount'])
                 respParameters['Remaining']    = 0xffff
                 respParameters['DataCount']    = len(content)
                 respParameters['DataOffset']   = 59
                 respParameters['DataCount_Hi'] = 0
                 respData = content
             except Exception as e:
                 smbServer.log('smbComReadAndX: %s ' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbQueryInformation(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION)
        respParameters = smb.SMBQueryInformationResponse_Parameters()
        respData       = ''

        queryInformation= smb.SMBQueryInformation_Data(flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
            fileSize, lastWriteTime, fileAttributes = queryFsInformation(
                connData['ConnectedShares'][recvPacket['Tid']]['path'],
                decodeSMBString(recvPacket['Flags2'],queryInformation['FileName']))

            respParameters['FileSize']       = fileSize
            respParameters['LastWriteTime']  = lastWriteTime
            respParameters['FileAttributes'] = fileAttributes
            errorCode = STATUS_SUCCESS
        else:
            # STATUS_SMB_BAD_TID
            errorCode = STATUS_SMB_BAD_TID
            respParameters  = ''
            respData        = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbQueryInformationDisk(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION_DISK)
        respParameters = smb.SMBQueryInformationDiskResponse_Parameters()
        respData       = ''

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
            totalUnits, freeUnits = queryDiskInformation(
                        connData['ConnectedShares'][recvPacket['Tid']]['path'])

            respParameters['TotalUnits']    = totalUnits
            respParameters['BlocksPerUnit'] = 1
            respParameters['BlockSize']     = 1
            respParameters['FreeUnits']     = freeUnits
            errorCode = STATUS_SUCCESS
        else:
            # STATUS_SMB_BAD_TID
            respData  = ''
            respParameters = ''
            errorCode = STATUS_SMB_BAD_TID


        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComEcho(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO)
        respParameters = smb.SMBEchoResponse_Parameters()
        respData       = smb.SMBEchoResponse_Data()

        echoData       = smb.SMBEcho_Data(SMBCommand['Data'])

        respParameters['SequenceNumber'] = 1
        respData['Data']                 = echoData['Data']

        respSMBCommand['Parameters']     = respParameters
        respSMBCommand['Data']           = respData

        errorCode = STATUS_SUCCESS
        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComTreeDisconnect(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_DISCONNECT)

        # Check if the Tid matches the Tid trying to disconnect
        respParameters = ''
        respData = ''

        if recvPacket['Tid'] in connData['ConnectedShares']:
            smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['Tid'],connData['ConnectedShares'][recvPacket['Tid']]['shareName']))
            del(connData['ConnectedShares'][recvPacket['Tid']])
            errorCode = STATUS_SUCCESS
        else:
            # STATUS_SMB_BAD_TID
            errorCode = STATUS_SMB_BAD_TID

        respSMBCommand['Parameters'] = respParameters
        respSMBCommand['Data']       = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComLogOffAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_LOGOFF_ANDX)

        # Check if the Uid matches the user trying to logoff
        respParameters = ''
        respData = ''
        if recvPacket['Uid'] != connData['Uid']:
            # STATUS_SMB_BAD_UID
            errorCode = STATUS_SMB_BAD_UID
        else:
            errorCode = STATUS_SUCCESS

        respSMBCommand['Parameters']   = respParameters
        respSMBCommand['Data']         = respData
        connData['Uid'] = 0

        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComQueryInformation2(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_QUERY_INFORMATION2)
        respParameters        = smb.SMBQueryInformation2Response_Parameters()
        respData              = ''

        queryInformation2 = smb.SMBQueryInformation2_Parameters(SMBCommand['Parameters'])
        errorCode = 0xFF
        if queryInformation2['Fid'] in connData['OpenedFiles']:
             errorCode = STATUS_SUCCESS
             pathName = connData['OpenedFiles'][queryInformation2['Fid']]['FileName']
             try:
                 (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(pathName)
                 respParameters['CreateDate']         = getSMBDate(ctime)
                 respParameters['CreationTime']       = getSMBTime(ctime)
                 respParameters['LastAccessDate']     = getSMBDate(atime)
                 respParameters['LastAccessTime']     = getSMBTime(atime)
                 respParameters['LastWriteDate']      = getSMBDate(mtime)
                 respParameters['LastWriteTime']      = getSMBTime(mtime)
                 respParameters['FileDataSize']       = size
                 respParameters['FileAllocationSize'] = size
                 attribs = 0
                 if os.path.isdir(pathName):
                     attribs = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
                 if os.path.isfile(pathName):
                     attribs = smb.SMB_FILE_ATTRIBUTE_NORMAL
                 respParameters['FileAttributes'] = attribs
             except Exception as e:
                 smbServer.log('smbComQueryInformation2 %s' % e,logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED

        if errorCode > 0:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket):
        # TODO: Fully implement this
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX)
        respParameters        = smb.SMBNtCreateAndXResponse_Parameters()
        respData              = ''

        ntCreateAndXParameters = smb.SMBNtCreateAndX_Parameters(SMBCommand['Parameters'])
        ntCreateAndXData       = smb.SMBNtCreateAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        #if ntCreateAndXParameters['CreateFlags'] & 0x10:  # NT_CREATE_REQUEST_EXTENDED_RESPONSE
        #    respParameters        = smb.SMBNtCreateAndXExtendedResponse_Parameters()
        #    respParameters['VolumeGUID'] = '\x00'

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             # If we have a rootFid, the path is relative to that fid
             errorCode = STATUS_SUCCESS
             if ntCreateAndXParameters['RootFid'] > 0:
                 path = connData['OpenedFiles'][ntCreateAndXParameters['RootFid']]['FileName']
                 LOG.debug("RootFid present %s!" % path)
             else:
                 if 'path' in connData['ConnectedShares'][recvPacket['Tid']]:
                     path = connData['ConnectedShares'][recvPacket['Tid']]['path']
                 else:
                     path = 'NONE'
                     errorCode = STATUS_ACCESS_DENIED

             deleteOnClose = False

             fileName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/'))
             if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
                # strip leading '/'
                fileName = fileName[1:]
             pathName = os.path.join(path,fileName)
             createDisposition = ntCreateAndXParameters['Disposition']
             mode = 0

             if createDisposition == smb.FILE_SUPERSEDE:
                 mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb.FILE_OVERWRITE_IF == smb.FILE_OVERWRITE_IF:
                 mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb.FILE_OVERWRITE == smb.FILE_OVERWRITE:
                 if os.path.exists(pathName) is True:
                     mode |= os.O_TRUNC
                 else:
                     errorCode = STATUS_NO_SUCH_FILE
             elif createDisposition & smb.FILE_OPEN_IF == smb.FILE_OPEN_IF:
                 if os.path.exists(pathName) is True:
                     mode |= os.O_TRUNC
                 else:
                     mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb.FILE_CREATE == smb.FILE_CREATE:
                 if os.path.exists(pathName) is True:
                     errorCode = STATUS_OBJECT_NAME_COLLISION
                 else:
                     mode |= os.O_CREAT
             elif createDisposition & smb.FILE_OPEN == smb.FILE_OPEN:
                 if os.path.exists(pathName) is not True and (unicode(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
                     errorCode = STATUS_NO_SUCH_FILE

             if errorCode == STATUS_SUCCESS:
                 desiredAccess = ntCreateAndXParameters['AccessMask']
                 if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
                     mode |= os.O_RDONLY
                 if (desiredAccess & smb.FILE_WRITE_DATA) or (desiredAccess & smb.GENERIC_WRITE):
                     if (desiredAccess & smb.FILE_READ_DATA) or (desiredAccess & smb.GENERIC_READ):
                         mode |= os.O_RDWR #| os.O_APPEND
                     else:
                         mode |= os.O_WRONLY #| os.O_APPEND
                 if desiredAccess & smb.GENERIC_ALL:
                     mode |= os.O_RDWR #| os.O_APPEND

                 createOptions =  ntCreateAndXParameters['CreateOptions']
                 if mode & os.O_CREAT == os.O_CREAT:
                     if createOptions & smb.FILE_DIRECTORY_FILE == smb.FILE_DIRECTORY_FILE:
                         try:
                             # Let's create the directory
                             os.mkdir(pathName)
                             mode = os.O_RDONLY
                         except Exception as e:
                             smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR)
                             errorCode = STATUS_ACCESS_DENIED
                 if createOptions & smb.FILE_NON_DIRECTORY_FILE == smb.FILE_NON_DIRECTORY_FILE:
                     # If the file being opened is a directory, the server MUST fail the request with
                     # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
                     # response.
                     if os.path.isdir(pathName) is True:
                        errorCode = STATUS_FILE_IS_A_DIRECTORY

                 if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
                     deleteOnClose = True

                 if errorCode == STATUS_SUCCESS:
                     try:
                         if os.path.isdir(pathName) and sys.platform == 'win32':
                            fid = VOID_FILE_DESCRIPTOR
                         else:
                            if sys.platform == 'win32':
                               mode |= os.O_BINARY
                            if unicode(pathName) in smbServer.getRegisteredNamedPipes():
                                fid = PIPE_FILE_DESCRIPTOR
                                sock = socket.socket()
                                sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)])
                            else:
                                fid = os.open(pathName, mode)
                     except Exception as e:
                         smbServer.log("NTCreateAndX: %s,%s,%s" % (pathName,mode,e),logging.ERROR)
                         #print e
                         fid = 0
                         errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID

        if errorCode == STATUS_SUCCESS:
            # Simple way to generate a fid
            if len(connData['OpenedFiles']) == 0:
               fakefid = 1
            else:
               fakefid = connData['OpenedFiles'].keys()[-1] + 1
            respParameters['Fid'] = fakefid
            respParameters['CreateAction'] = createDisposition
            if fid == PIPE_FILE_DESCRIPTOR:
                respParameters['FileAttributes'] = 0x80
                respParameters['IsDirectory'] = 0
                respParameters['CreateTime']     = 0
                respParameters['LastAccessTime'] = 0
                respParameters['LastWriteTime']  = 0
                respParameters['LastChangeTime'] = 0
                respParameters['AllocationSize'] = 4096
                respParameters['EndOfFile']      = 0
                respParameters['FileType']       = 2
                respParameters['IPCState']       = 0x5ff
            else:
                if os.path.isdir(pathName):
                    respParameters['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
                    respParameters['IsDirectory'] = 1
                else:
                    respParameters['IsDirectory'] = 0
                    respParameters['FileAttributes'] = ntCreateAndXParameters['FileAttributes']
                # Let's get this file's information
                respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO)
                if errorCode == STATUS_SUCCESS:
                    respParameters['CreateTime']     = respInfo['CreationTime']
                    respParameters['LastAccessTime'] = respInfo['LastAccessTime']
                    respParameters['LastWriteTime']  = respInfo['LastWriteTime']
                    respParameters['LastChangeTime'] = respInfo['LastChangeTime']
                    respParameters['FileAttributes'] = respInfo['ExtFileAttributes']
                    respParameters['AllocationSize'] = respInfo['AllocationSize']
                    respParameters['EndOfFile']      = respInfo['EndOfFile']
                else:
                    respParameters = ''
                    respData       = ''

            if errorCode == STATUS_SUCCESS:
                # Let's store the fid for the connection
                # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
                connData['OpenedFiles'][fakefid] = {}
                connData['OpenedFiles'][fakefid]['FileHandle'] = fid
                connData['OpenedFiles'][fakefid]['FileName'] = pathName
                connData['OpenedFiles'][fakefid]['DeleteOnClose']  = deleteOnClose
                if fid == PIPE_FILE_DESCRIPTOR:
                    connData['OpenedFiles'][fakefid]['Socket'] = sock
        else:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComOpenAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_OPEN_ANDX)
        respParameters        = smb.SMBOpenAndXResponse_Parameters()
        respData              = ''

        openAndXParameters = smb.SMBOpenAndX_Parameters(SMBCommand['Parameters'])
        openAndXData       = smb.SMBOpenAndX_Data( flags = recvPacket['Flags2'], data = SMBCommand['Data'])

        # Get the Tid associated
        if recvPacket['Tid'] in connData['ConnectedShares']:
             path = connData['ConnectedShares'][recvPacket['Tid']]['path']
             openedFile, mode, pathName, errorCode = openFile(path,
                     decodeSMBString(recvPacket['Flags2'],openAndXData['FileName']),
                     openAndXParameters['DesiredAccess'],
                     openAndXParameters['FileAttributes'],
                     openAndXParameters['OpenMode'])
        else:
           errorCode = STATUS_SMB_BAD_TID

        if errorCode == STATUS_SUCCESS:
            # Simple way to generate a fid
            fid = len(connData['OpenedFiles']) + 1
            if len(connData['OpenedFiles']) == 0:
               fid = 1
            else:
               fid = connData['OpenedFiles'].keys()[-1] + 1
            respParameters['Fid'] = fid
            if mode & os.O_CREAT:
                # File did not exist and was created
                respParameters['Action'] = 0x2
            elif mode & os.O_RDONLY:
                # File existed and was opened
                respParameters['Action'] = 0x1
            elif mode & os.O_APPEND:
                # File existed and was opened
                respParameters['Action'] = 0x1
            else:
                # File existed and was truncated
                respParameters['Action'] = 0x3

            # Let's store the fid for the connection
            #smbServer.log('Opening file %s' % pathName)
            connData['OpenedFiles'][fid] = {}
            connData['OpenedFiles'][fid]['FileHandle'] = openedFile
            connData['OpenedFiles'][fid]['FileName'] = pathName
            connData['OpenedFiles'][fid]['DeleteOnClose']  = False
        else:
            respParameters = ''
            respData       = ''

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

        resp = smb.NewSMBPacket()
        resp['Flags1'] = smb.SMB.FLAGS1_REPLY
        resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE

        resp['Tid'] = recvPacket['Tid']
        resp['Mid'] = recvPacket['Mid']
        resp['Pid'] = connData['Pid']

        respSMBCommand        = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
        respParameters        = smb.SMBTreeConnectAndXResponse_Parameters()
        respData              = smb.SMBTreeConnectAndXResponse_Data()

        treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])

        if treeConnectAndXParameters['Flags'] & 0x8:
            respParameters        = smb.SMBTreeConnectAndXExtendedResponse_Parameters()

        treeConnectAndXData                    = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )
        treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
        treeConnectAndXData.fromString(SMBCommand['Data'])

        errorCode = STATUS_SUCCESS

        ## Process here the request, does the share exist?
        UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])

        # Is this a UNC?
        if ntpath.ismount(UNCOrShare):
            path = UNCOrShare.split('\\')[3]
        else:
            path = ntpath.basename(UNCOrShare)

        share = searchShare(connId, path, smbServer)
        if share is not None:
            # Simple way to generate a Tid
            if len(connData['ConnectedShares']) == 0:
               tid = 1
            else:
               tid = connData['ConnectedShares'].keys()[-1] + 1
            connData['ConnectedShares'][tid] = share
            connData['ConnectedShares'][tid]['shareName'] = path
            resp['Tid'] = tid
            #smbServer.log("Connecting Share(%d:%s)" % (tid,path))
        else:
            smbServer.log("TreeConnectAndX not found %s" % path, logging.ERROR)
            errorCode = STATUS_OBJECT_PATH_NOT_FOUND
            resp['ErrorCode']   = errorCode >> 16
            resp['ErrorClass']  = errorCode & 0xff
        ##
        respParameters['OptionalSupport'] = smb.SMB.SMB_SUPPORT_SEARCH_BITS

        if path == 'IPC$':
            respData['Service']               = 'IPC'
        else:
            respData['Service']               = path
        respData['PadLen']                = 0
        respData['NativeFileSystem']      = encodeSMBString(recvPacket['Flags2'], 'NTFS' )

        respSMBCommand['Parameters']             = respParameters
        respSMBCommand['Data']                   = respData

        resp['Uid'] = connData['Uid']
        resp.addCommand(respSMBCommand)
        smbServer.setConnectionData(connId, connData)

        return None, [resp], errorCode

    @staticmethod
    def smbComSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId, checkStatus = False)

        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)

        # From [MS-SMB]
        # When extended security is being used (see section 3.2.4.2.4), the
        # request MUST take the following form
        # [..]
        # WordCount (1 byte): The value of this field MUST be 0x0C.
        if SMBCommand['WordCount'] == 12:
            # Extended security. Here we deal with all SPNEGO stuff
            respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()
            respData       = smb.SMBSessionSetupAndX_Extended_Response_Data(flags = recvPacket['Flags2'])
            sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
            sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
            sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
            sessionSetupData.fromString(SMBCommand['Data'])
            connData['Capabilities'] = sessionSetupParameters['Capabilities']

            rawNTLM = False
            if struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_AID:
               # NEGOTIATE packet
               blob =  SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
               token = blob['MechToken']
               if len(blob['MechTypes'][0]) > 0:
                   # Is this GSSAPI NTLM or something else we don't support?
                   mechType = blob['MechTypes'][0]
                   if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
                       # Nope, do we know it?
                       if mechType in MechTypes:
                           mechStr = MechTypes[mechType]
                       else:
                           mechStr = hexlify(mechType)
                       smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
                       # We don't know the token, we answer back again saying
                       # we just support NTLM.
                       # ToDo: Build this into a SPNEGO_NegTokenResp()
                       respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'
                       respParameters['SecurityBlobLength'] = len(respToken)
                       respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
                       respData['SecurityBlob']       = respToken
                       respData['NativeOS']     = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
                       respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
                       respSMBCommand['Parameters'] = respParameters
                       respSMBCommand['Data']       = respData
                       return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

            elif struct.unpack('B',sessionSetupData['SecurityBlob'][0])[0] == ASN1_SUPPORTED_MECH:
               # AUTH packet
               blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
               token = blob['ResponseToken']
            else:
               # No GSSAPI stuff, raw NTLMSSP
               rawNTLM = True
               token = sessionSetupData['SecurityBlob']

            # Here we only handle NTLMSSP, depending on what stage of the
            # authentication we are, we act on it
            messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]

            if messageType == 0x01:
                # NEGOTIATE_MESSAGE
                negotiateMessage = ntlm.NTLMAuthNegotiate()
                negotiateMessage.fromString(token)
                # Let's store it in the connection data
                connData['NEGOTIATE_MESSAGE'] = negotiateMessage
                # Let's build the answer flags
                # TODO: Parse all the flags. With this we're leaving some clients out

                ansFlags = 0

                if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
                   ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
                if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
                   ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
                if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
                   ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
                if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
                   ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
                if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
                   ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
                if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
                   ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

                ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET

                # Generate the AV_PAIRS
                av_pairs = ntlm.AV_PAIRS()
                # TODO: Put the proper data from SMBSERVER config
                av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
                av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
                av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )

                challengeMessage = ntlm.NTLMAuthChallenge()
                challengeMessage['flags']            = ansFlags
                challengeMessage['domain_len']       = len(smbServer.getServerDomain().encode('utf-16le'))
                challengeMessage['domain_max_len']   = challengeMessage['domain_len']
                challengeMessage['domain_offset']    = 40 + 16
                challengeMessage['challenge']        = smbServer.getSMBChallenge()
                challengeMessage['domain_name']      = smbServer.getServerDomain().encode('utf-16le')
                challengeMessage['TargetInfoFields_len']     = len(av_pairs)
                challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
                challengeMessage['TargetInfoFields'] = av_pairs
                challengeMessage['TargetInfoFields_offset']  = 40 + 16 + len(challengeMessage['domain_name'])
                challengeMessage['Version']          = '\xff'*8
                challengeMessage['VersionLen']       = 8

                if rawNTLM is False:
                    respToken = SPNEGO_NegTokenResp()
                    # accept-incomplete. We want more data
                    respToken['NegResult'] = '\x01'
                    respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

                    respToken['ResponseToken'] = challengeMessage.getData()
                else:
                    respToken = challengeMessage

                # Setting the packet to STATUS_MORE_PROCESSING
                errorCode = STATUS_MORE_PROCESSING_REQUIRED
                # Let's set up an UID for this connection and store it
                # in the connection's data
                # Picking a fixed value
                # TODO: Manage more UIDs for the same session
                connData['Uid'] = 10
                # Let's store it in the connection data
                connData['CHALLENGE_MESSAGE'] = challengeMessage

            elif messageType == 0x02:
                # CHALLENGE_MESSAGE
                raise Exception('Challenge Message raise, not implemented!')
            elif messageType == 0x03:
                # AUTHENTICATE_MESSAGE, here we deal with authentication
                authenticateMessage = ntlm.NTLMAuthChallengeResponse()
                authenticateMessage.fromString(token)
                smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name']))
                # TODO: Check the credentials! Now granting permissions

                respToken = SPNEGO_NegTokenResp()
                # accept-completed
                respToken['NegResult'] = '\x00'

                # Status SUCCESS
                errorCode = STATUS_SUCCESS
                smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
                # Let's store it in the connection data
                connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
                try:
                    jtr_dump_path = smbServer.getJTRdumpPath()
                    ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] )
                    smbServer.log(ntlm_hash_data['hash_string'])
                    if jtr_dump_path is not '':
                        writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
                except:
                    smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
            else:
                raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

            respParameters['SecurityBlobLength'] = len(respToken)
            respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
            respData['SecurityBlob']       = respToken.getData()

        else:
            # Process Standard Security
            respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
            respData       = smb.SMBSessionSetupAndXResponse_Data()
            sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
            sessionSetupData = smb.SMBSessionSetupAndX_Data()
            sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
            sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
            sessionSetupData.fromString(SMBCommand['Data'])
            connData['Capabilities'] = sessionSetupParameters['Capabilities']
            # Do the verification here, for just now we grant access
            # TODO: Manage more UIDs for the same session
            errorCode = STATUS_SUCCESS
            connData['Uid'] = 10
            respParameters['Action'] = 0
            smbServer.log('User %s\\%s authenticated successfully (basic)' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account']))
            try:
                jtr_dump_path = smbServer.getJTRdumpPath()
                ntlm_hash_data = outputToJohnFormat( '', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'], sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'] )
                smbServer.log(ntlm_hash_data['hash_string'])
                if jtr_dump_path is not '':
                    writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
            except:
                smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)

        respData['NativeOS']     = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
        respData['NativeLanMan'] = encodeSMBString(recvPacket['Flags2'], smbServer.getServerOS())
        respSMBCommand['Parameters'] = respParameters
        respSMBCommand['Data']       = respData

        # From now on, the client can ask for other commands
        connData['Authenticated'] = True
        # For now, just switching to nobody
        #os.setregid(65534,65534)
        #os.setreuid(65534,65534)
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smbComNegotiate(connId, smbServer, SMBCommand, recvPacket ):
        connData = smbServer.getConnectionData(connId, checkStatus = False)
        connData['Pid'] = recvPacket['Pid']

        SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
        respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_NEGOTIATE)

        resp = smb.NewSMBPacket()
        resp['Flags1'] = smb.SMB.FLAGS1_REPLY
        resp['Pid'] = connData['Pid']
        resp['Tid'] = recvPacket['Tid']
        resp['Mid'] = recvPacket['Mid']

        # TODO: We support more dialects, and parse them accordingly
        dialects = SMBCommand['Data'].split('\x02')
        try:
           index = dialects.index('NT LM 0.12\x00') - 1
           # Let's fill the data for NTLM
           if recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY:
                    resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
                    #resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS
                    _dialects_data = smb.SMBExtended_Security_Data()
                    _dialects_data['ServerGUID'] = 'A'*16
                    blob = SPNEGO_NegTokenInit()
                    blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
                    _dialects_data['SecurityBlob'] = blob.getData()

                    _dialects_parameters = smb.SMBExtended_Security_Parameters()
                    _dialects_parameters['Capabilities']    = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS | smb.SMB.CAP_UNICODE
                    _dialects_parameters['ChallengeLength'] = 0

           else:
                    resp['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_UNICODE
                    _dialects_parameters = smb.SMBNTLMDialect_Parameters()
                    _dialects_data= smb.SMBNTLMDialect_Data()
                    _dialects_data['Payload'] = ''
                    if 'EncryptionKey' in connData:
                        _dialects_data['Challenge'] = connData['EncryptionKey']
                        _dialects_parameters['ChallengeLength'] = len(str(_dialects_data))
                    else:
                        # TODO: Handle random challenges, now one that can be used with rainbow tables
                        _dialects_data['Challenge'] = '\x11\x22\x33\x44\x55\x66\x77\x88'
                        _dialects_parameters['ChallengeLength'] = 8
                    _dialects_parameters['Capabilities']    = smb.SMB.CAP_USE_NT_ERRORS | smb.SMB.CAP_NT_SMBS

           # Let's see if we need to support RPC_REMOTE_APIS
           config = smbServer.getServerConfig()
           if config.has_option('global','rpc_apis'):
               if config.getboolean('global', 'rpc_apis') is True:
                  _dialects_parameters['Capabilities'] |= smb.SMB.CAP_RPC_REMOTE_APIS

           _dialects_parameters['DialectIndex']    = index
           _dialects_parameters['SecurityMode']    = smb.SMB.SECURITY_AUTH_ENCRYPTED | smb.SMB.SECURITY_SHARE_USER
           _dialects_parameters['MaxMpxCount']     = 1
           _dialects_parameters['MaxNumberVcs']    = 1
           _dialects_parameters['MaxBufferSize']   = 64000
           _dialects_parameters['MaxRawSize']      = 65536
           _dialects_parameters['SessionKey']      = 0
           _dialects_parameters['LowDateTime']     = 0
           _dialects_parameters['HighDateTime']    = 0
           _dialects_parameters['ServerTimeZone']  = 0


           respSMBCommand['Data']           = _dialects_data
           respSMBCommand['Parameters']     = _dialects_parameters
           connData['_dialects_data']       = _dialects_data
           connData['_dialects_parameters'] = _dialects_parameters

        except Exception as e:
           # No NTLM throw an error
           smbServer.log('smbComNegotiate: %s' % e, logging.ERROR)
           respSMBCommand['Data'] = struct.pack('<H',0xffff)


        smbServer.setConnectionData(connId, connData)

        resp.addCommand(respSMBCommand)

        return None, [resp], STATUS_SUCCESS

    @staticmethod
    def default(connId, smbServer, SMBCommand, recvPacket):
        # By default we return an SMB Packet with error not implemented
        smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG)
        packet = smb.NewSMBPacket()
        packet['Flags1']  = smb.SMB.FLAGS1_REPLY
        packet['Flags2']  = smb.SMB.FLAGS2_NT_STATUS
        packet['Command'] = recvPacket['Command']
        packet['Pid']     = recvPacket['Pid']
        packet['Tid']     = recvPacket['Tid']
        packet['Mid']     = recvPacket['Mid']
        packet['Uid']     = recvPacket['Uid']
        packet['Data']    = '\x00\x00\x00'
        errorCode = STATUS_NOT_IMPLEMENTED
        packet['ErrorCode']   = errorCode >> 16
        packet['ErrorClass']  = errorCode & 0xff

        return None, [packet], errorCode

class SMB2Commands:
    @staticmethod
    def smb2Negotiate(connId, smbServer, recvPacket, isSMB1 = False):
        connData = smbServer.getConnectionData(connId, checkStatus = False)

        respPacket = smb2.SMB2Packet()
        respPacket['Flags']     = smb2.SMB2_FLAGS_SERVER_TO_REDIR
        respPacket['Status']    = STATUS_SUCCESS
        respPacket['CreditRequestResponse'] = 1
        respPacket['Command']   = smb2.SMB2_NEGOTIATE
        respPacket['SessionID'] = 0
        if isSMB1 is False:
            respPacket['MessageID'] = recvPacket['MessageID']
        else:
            respPacket['MessageID'] = 0
        respPacket['TreeID']    = 0


        respSMBCommand = smb2.SMB2Negotiate_Response()

        respSMBCommand['SecurityMode'] = 1
        if isSMB1 is True:
            # Let's first parse the packet to see if the client supports SMB2
            SMBCommand = smb.SMBCommand(recvPacket['Data'][0])

            dialects = SMBCommand['Data'].split('\x02')
            if 'SMB 2.002\x00' in dialects or 'SMB 2.???\x00' in dialects:
                respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
            else:
                # Client does not support SMB2 fallbacking
                raise Exception('SMB2 not supported, fallbacking')
        else:
            respSMBCommand['DialectRevision'] = smb2.SMB2_DIALECT_002
        respSMBCommand['ServerGuid'] = 'A'*16
        respSMBCommand['Capabilities'] = 0
        respSMBCommand['MaxTransactSize'] = 65536
        respSMBCommand['MaxReadSize'] = 65536
        respSMBCommand['MaxWriteSize'] = 65536
        respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))
        respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))
        respSMBCommand['SecurityBufferOffset'] = 0x80

        blob = SPNEGO_NegTokenInit()
        blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]

        respSMBCommand['Buffer'] = blob.getData()
        respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])

        respPacket['Data']      = respSMBCommand

        smbServer.setConnectionData(connId, connData)

        return None, [respPacket], STATUS_SUCCESS

    @staticmethod
    def smb2SessionSetup(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId, checkStatus = False)

        respSMBCommand = smb2.SMB2SessionSetup_Response()

        sessionSetupData = smb2.SMB2SessionSetup(recvPacket['Data'])

        connData['Capabilities'] = sessionSetupData['Capabilities']

        securityBlob = sessionSetupData['Buffer']

        rawNTLM = False
        if struct.unpack('B',securityBlob[0])[0] == ASN1_AID:
           # NEGOTIATE packet
           blob =  SPNEGO_NegTokenInit(securityBlob)
           token = blob['MechToken']
           if len(blob['MechTypes'][0]) > 0:
               # Is this GSSAPI NTLM or something else we don't support?
               mechType = blob['MechTypes'][0]
               if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']:
                   # Nope, do we know it?
                   if mechType in MechTypes:
                       mechStr = MechTypes[mechType]
                   else:
                       mechStr = hexlify(mechType)
                   smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
                   # We don't know the token, we answer back again saying
                   # we just support NTLM.
                   # ToDo: Build this into a SPNEGO_NegTokenResp()
                   respToken = '\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'
                   respSMBCommand['SecurityBufferOffset'] = 0x48
                   respSMBCommand['SecurityBufferLength'] = len(respToken)
                   respSMBCommand['Buffer'] = respToken

                   return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
        elif struct.unpack('B',securityBlob[0])[0] == ASN1_SUPPORTED_MECH:
           # AUTH packet
           blob = SPNEGO_NegTokenResp(securityBlob)
           token = blob['ResponseToken']
        else:
           # No GSSAPI stuff, raw NTLMSSP
           rawNTLM = True
           token = securityBlob

        # Here we only handle NTLMSSP, depending on what stage of the
        # authentication we are, we act on it
        messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]

        if messageType == 0x01:
            # NEGOTIATE_MESSAGE
            negotiateMessage = ntlm.NTLMAuthNegotiate()
            negotiateMessage.fromString(token)
            # Let's store it in the connection data
            connData['NEGOTIATE_MESSAGE'] = negotiateMessage
            # Let's build the answer flags
            # TODO: Parse all the flags. With this we're leaving some clients out

            ansFlags = 0

            if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_56:
               ansFlags |= ntlm.NTLMSSP_NEGOTIATE_56
            if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_128:
               ansFlags |= ntlm.NTLMSSP_NEGOTIATE_128
            if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH:
               ansFlags |= ntlm.NTLMSSP_NEGOTIATE_KEY_EXCH
            if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY:
               ansFlags |= ntlm.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
            if negotiateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
               ansFlags |= ntlm.NTLMSSP_NEGOTIATE_UNICODE
            if negotiateMessage['flags'] & ntlm.NTLM_NEGOTIATE_OEM:
               ansFlags |= ntlm.NTLM_NEGOTIATE_OEM

            ansFlags |= ntlm.NTLMSSP_NEGOTIATE_VERSION | ntlm.NTLMSSP_NEGOTIATE_TARGET_INFO | ntlm.NTLMSSP_TARGET_TYPE_SERVER | ntlm.NTLMSSP_NEGOTIATE_NTLM | ntlm.NTLMSSP_REQUEST_TARGET

            # Generate the AV_PAIRS
            av_pairs = ntlm.AV_PAIRS()
            # TODO: Put the proper data from SMBSERVER config
            av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] = smbServer.getServerName().encode('utf-16le')
            av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] = smbServer.getServerDomain().encode('utf-16le')
            av_pairs[ntlm.NTLMSSP_AV_TIME] = struct.pack('<q', (116444736000000000 + calendar.timegm(time.gmtime()) * 10000000) )

            challengeMessage = ntlm.NTLMAuthChallenge()
            challengeMessage['flags']            = ansFlags
            challengeMessage['domain_len']       = len(smbServer.getServerDomain().encode('utf-16le'))
            challengeMessage['domain_max_len']   = challengeMessage['domain_len']
            challengeMessage['domain_offset']    = 40 + 16
            challengeMessage['challenge']        = smbServer.getSMBChallenge()
            challengeMessage['domain_name']      = smbServer.getServerDomain().encode('utf-16le')
            challengeMessage['TargetInfoFields_len']     = len(av_pairs)
            challengeMessage['TargetInfoFields_max_len'] = len(av_pairs)
            challengeMessage['TargetInfoFields'] = av_pairs
            challengeMessage['TargetInfoFields_offset']  = 40 + 16 + len(challengeMessage['domain_name'])
            challengeMessage['Version']          = '\xff'*8
            challengeMessage['VersionLen']       = 8

            if rawNTLM is False:
                respToken = SPNEGO_NegTokenResp()
                # accept-incomplete. We want more data
                respToken['NegResult'] = '\x01'
                respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

                respToken['ResponseToken'] = challengeMessage.getData()
            else:
                respToken = challengeMessage

            # Setting the packet to STATUS_MORE_PROCESSING
            errorCode = STATUS_MORE_PROCESSING_REQUIRED
            # Let's set up an UID for this connection and store it
            # in the connection's data
            # Picking a fixed value
            # TODO: Manage more UIDs for the same session
            connData['Uid'] = random.randint(1,0xffffffff)
            # Let's store it in the connection data
            connData['CHALLENGE_MESSAGE'] = challengeMessage

        elif messageType == 0x02:
            # CHALLENGE_MESSAGE
            raise Exception('Challenge Message raise, not implemented!')
        elif messageType == 0x03:
            # AUTHENTICATE_MESSAGE, here we deal with authentication
            authenticateMessage = ntlm.NTLMAuthChallengeResponse()
            authenticateMessage.fromString(token)
            smbServer.log("AUTHENTICATE_MESSAGE (%s\\%s,%s)" % (authenticateMessage['domain_name'], authenticateMessage['user_name'], authenticateMessage['host_name']))
            # TODO: Check the credentials! Now granting permissions

            respToken = SPNEGO_NegTokenResp()
            # accept-completed
            respToken['NegResult'] = '\x00'

            # Status SUCCESS
            errorCode = STATUS_SUCCESS
            smbServer.log('User %s\\%s authenticated successfully' % (authenticateMessage['user_name'], authenticateMessage['host_name']))
            # Let's store it in the connection data
            connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
            try:
                jtr_dump_path = smbServer.getJTRdumpPath()
                ntlm_hash_data = outputToJohnFormat( connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], authenticateMessage['domain_name'], authenticateMessage['lanman'], authenticateMessage['ntlm'] )
                smbServer.log(ntlm_hash_data['hash_string'])
                if jtr_dump_path is not '':
                    writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], jtr_dump_path)
            except:
                smbServer.log("Could not write NTLM Hashes to the specified JTR_Dump_Path %s" % jtr_dump_path)
            respSMBCommand['SessionFlags'] = 1
        else:
            raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

        respSMBCommand['SecurityBufferOffset'] = 0x48
        respSMBCommand['SecurityBufferLength'] = len(respToken)
        respSMBCommand['Buffer'] = respToken.getData()

        # From now on, the client can ask for other commands
        connData['Authenticated'] = True
        # For now, just switching to nobody
        #os.setregid(65534,65534)
        #os.setreuid(65534,65534)
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2TreeConnect(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respPacket = smb2.SMB2Packet()
        respPacket['Flags']     = smb2.SMB2_FLAGS_SERVER_TO_REDIR
        respPacket['Status']    = STATUS_SUCCESS
        respPacket['CreditRequestResponse'] = 1
        respPacket['Command']   = recvPacket['Command']
        respPacket['SessionID'] = connData['Uid']
        respPacket['Reserved']  = recvPacket['Reserved']
        respPacket['MessageID'] = recvPacket['MessageID']
        respPacket['TreeID']    = recvPacket['TreeID']

        respSMBCommand        = smb2.SMB2TreeConnect_Response()

        treeConnectRequest = smb2.SMB2TreeConnect(recvPacket['Data'])

        errorCode = STATUS_SUCCESS

        ## Process here the request, does the share exist?
        path = str(recvPacket)[treeConnectRequest['PathOffset']:][:treeConnectRequest['PathLength']]
        UNCOrShare = path.decode('utf-16le')

        # Is this a UNC?
        if ntpath.ismount(UNCOrShare):
            path = UNCOrShare.split('\\')[3]
        else:
            path = ntpath.basename(UNCOrShare)

        share = searchShare(connId, path.upper(), smbServer)
        if share is not None:
            # Simple way to generate a Tid
            if len(connData['ConnectedShares']) == 0:
               tid = 1
            else:
               tid = connData['ConnectedShares'].keys()[-1] + 1
            connData['ConnectedShares'][tid] = share
            connData['ConnectedShares'][tid]['shareName'] = path
            respPacket['TreeID']    = tid
            smbServer.log("Connecting Share(%d:%s)" % (tid,path))
        else:
            smbServer.log("SMB2_TREE_CONNECT not found %s" % path, logging.ERROR)
            errorCode = STATUS_OBJECT_PATH_NOT_FOUND
            respPacket['Status'] = errorCode
        ##

        if path == 'IPC$':
            respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_PIPE
            respSMBCommand['ShareFlags'] = 0x30
        else:
            respSMBCommand['ShareType'] = smb2.SMB2_SHARE_TYPE_DISK
            respSMBCommand['ShareFlags'] = 0x0

        respSMBCommand['Capabilities'] = 0
        respSMBCommand['MaximalAccess'] = 0x000f01ff

        respPacket['Data'] = respSMBCommand

        smbServer.setConnectionData(connId, connData)

        return None, [respPacket], errorCode

    @staticmethod
    def smb2Create(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb2.SMB2Create_Response()

        ntCreateRequest       = smb2.SMB2Create(recvPacket['Data'])

        respSMBCommand['Buffer'] = '\x00'
        # Get the Tid associated
        if recvPacket['TreeID'] in connData['ConnectedShares']:
             # If we have a rootFid, the path is relative to that fid
             errorCode = STATUS_SUCCESS
             if 'path' in connData['ConnectedShares'][recvPacket['TreeID']]:
                 path = connData['ConnectedShares'][recvPacket['TreeID']]['path']
             else:
                 path = 'NONE'
                 errorCode = STATUS_ACCESS_DENIED

             deleteOnClose = False

             fileName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))
             if len(fileName) > 0 and (fileName[0] == '/' or fileName[0] == '\\'):
                # strip leading '/'
                fileName = fileName[1:]
             pathName = os.path.join(path,fileName)
             createDisposition = ntCreateRequest['CreateDisposition']
             mode = 0

             if createDisposition == smb2.FILE_SUPERSEDE:
                 mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
                 mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
                 if os.path.exists(pathName) is True:
                     mode |= os.O_TRUNC
                 else:
                     errorCode = STATUS_NO_SUCH_FILE
             elif createDisposition & smb2.FILE_OPEN_IF == smb2.FILE_OPEN_IF:
                 if os.path.exists(pathName) is True:
                     mode |= os.O_TRUNC
                 else:
                     mode |= os.O_TRUNC | os.O_CREAT
             elif createDisposition & smb2.FILE_CREATE == smb2.FILE_CREATE:
                 if os.path.exists(pathName) is True:
                     errorCode = STATUS_OBJECT_NAME_COLLISION
                 else:
                     mode |= os.O_CREAT
             elif createDisposition & smb2.FILE_OPEN == smb2.FILE_OPEN:
                 if os.path.exists(pathName) is not True and (unicode(pathName) in smbServer.getRegisteredNamedPipes()) is not True:
                     errorCode = STATUS_NO_SUCH_FILE

             if errorCode == STATUS_SUCCESS:
                 desiredAccess = ntCreateRequest['DesiredAccess']
                 if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
                     mode |= os.O_RDONLY
                 if (desiredAccess & smb2.FILE_WRITE_DATA) or (desiredAccess & smb2.GENERIC_WRITE):
                     if (desiredAccess & smb2.FILE_READ_DATA) or (desiredAccess & smb2.GENERIC_READ):
                         mode |= os.O_RDWR #| os.O_APPEND
                     else:
                         mode |= os.O_WRONLY #| os.O_APPEND
                 if desiredAccess & smb2.GENERIC_ALL:
                     mode |= os.O_RDWR #| os.O_APPEND

                 createOptions =  ntCreateRequest['CreateOptions']
                 if mode & os.O_CREAT == os.O_CREAT:
                     if createOptions & smb2.FILE_DIRECTORY_FILE == smb2.FILE_DIRECTORY_FILE:
                         try:
                             # Let's create the directory
                             os.mkdir(pathName)
                             mode = os.O_RDONLY
                         except Exception as e:
                             smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR)
                             errorCode = STATUS_ACCESS_DENIED
                 if createOptions & smb2.FILE_NON_DIRECTORY_FILE == smb2.FILE_NON_DIRECTORY_FILE:
                     # If the file being opened is a directory, the server MUST fail the request with
                     # STATUS_FILE_IS_A_DIRECTORY in the Status field of the SMB Header in the server
                     # response.
                     if os.path.isdir(pathName) is True:
                        errorCode = STATUS_FILE_IS_A_DIRECTORY

                 if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
                     deleteOnClose = True

                 if errorCode == STATUS_SUCCESS:
                     try:
                         if os.path.isdir(pathName) and sys.platform == 'win32':
                            fid = VOID_FILE_DESCRIPTOR
                         else:
                            if sys.platform == 'win32':
                               mode |= os.O_BINARY
                            if unicode(pathName) in smbServer.getRegisteredNamedPipes():
                                fid = PIPE_FILE_DESCRIPTOR
                                sock = socket.socket()
                                sock.connect(smbServer.getRegisteredNamedPipes()[unicode(pathName)])
                            else:
                                fid = os.open(pathName, mode)
                     except Exception as e:
                         smbServer.log("SMB2_CREATE: %s,%s,%s" % (pathName,mode,e),logging.ERROR)
                         #print e
                         fid = 0
                         errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SMB_BAD_TID

        if errorCode == STATUS_SUCCESS:
            # Simple way to generate a fid
            fakefid = uuid.generate()

            respSMBCommand['FileID'] = fakefid
            respSMBCommand['CreateAction'] = createDisposition

            if fid == PIPE_FILE_DESCRIPTOR:
                respSMBCommand['CreationTime']   = 0
                respSMBCommand['LastAccessTime'] = 0
                respSMBCommand['LastWriteTime']  = 0
                respSMBCommand['ChangeTime']     = 0
                respSMBCommand['AllocationSize'] = 4096
                respSMBCommand['EndOfFile']      = 0
                respSMBCommand['FileAttributes'] = 0x80

            else:
                if os.path.isdir(pathName):
                    respSMBCommand['FileAttributes'] = smb.SMB_FILE_ATTRIBUTE_DIRECTORY
                else:
                    respSMBCommand['FileAttributes'] = ntCreateRequest['FileAttributes']
                # Let's get this file's information
                respInfo, errorCode = queryPathInformation('',pathName,level= smb.SMB_QUERY_FILE_ALL_INFO)
                if errorCode == STATUS_SUCCESS:
                    respSMBCommand['CreationTime']   = respInfo['CreationTime']
                    respSMBCommand['LastAccessTime'] = respInfo['LastAccessTime']
                    respSMBCommand['LastWriteTime']  = respInfo['LastWriteTime']
                    respSMBCommand['LastChangeTime'] = respInfo['LastChangeTime']
                    respSMBCommand['FileAttributes'] = respInfo['ExtFileAttributes']
                    respSMBCommand['AllocationSize'] = respInfo['AllocationSize']
                    respSMBCommand['EndOfFile']      = respInfo['EndOfFile']

            if errorCode == STATUS_SUCCESS:
                # Let's store the fid for the connection
                # smbServer.log('Create file %s, mode:0x%x' % (pathName, mode))
                connData['OpenedFiles'][fakefid] = {}
                connData['OpenedFiles'][fakefid]['FileHandle'] = fid
                connData['OpenedFiles'][fakefid]['FileName'] = pathName
                connData['OpenedFiles'][fakefid]['DeleteOnClose']  = deleteOnClose
                connData['OpenedFiles'][fakefid]['Open']  = {}
                connData['OpenedFiles'][fakefid]['Open']['EnumerationLocation'] = 0
                connData['OpenedFiles'][fakefid]['Open']['EnumerationSearchPattern'] = ''
                if fid == PIPE_FILE_DESCRIPTOR:
                    connData['OpenedFiles'][fakefid]['Socket'] = sock
        else:
            respSMBCommand = smb2.SMB2Error()

        if errorCode == STATUS_SUCCESS:
            connData['LastRequest']['SMB2_CREATE'] = respSMBCommand
        smbServer.setConnectionData(connId, connData)

        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Close(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb2.SMB2Close_Response()

        closeRequest = smb2.SMB2Close(recvPacket['Data'])

        if str(closeRequest['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(closeRequest['FileID'])
        else:
            fileID = str(closeRequest['FileID'])

        if fileID in connData['OpenedFiles']:
             errorCode = STATUS_SUCCESS
             fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
             pathName = connData['OpenedFiles'][fileID]['FileName']
             infoRecord = None
             try:
                 if fileHandle == PIPE_FILE_DESCRIPTOR:
                     connData['OpenedFiles'][fileID]['Socket'].close()
                 elif fileHandle != VOID_FILE_DESCRIPTOR:
                     os.close(fileHandle)
                     infoRecord, errorCode = queryFileInformation(os.path.dirname(pathName), os.path.basename(pathName), smb2.SMB2_FILE_NETWORK_OPEN_INFO)
             except Exception as e:
                 smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
                 errorCode = STATUS_INVALID_HANDLE
             else:
                 # Check if the file was marked for removal
                 if connData['OpenedFiles'][fileID]['DeleteOnClose'] is True:
                     try:
                         if os.path.isdir(pathName):
                             shutil.rmtree(connData['OpenedFiles'][fileID]['FileName'])
                         else:
                             os.remove(connData['OpenedFiles'][fileID]['FileName'])
                     except Exception as e:
                         smbServer.log("SMB2_CLOSE %s" % e, logging.ERROR)
                         errorCode = STATUS_ACCESS_DENIED

                 # Now fill out the response
                 if infoRecord is not None:
                     respSMBCommand['CreationTime']   = infoRecord['CreationTime']
                     respSMBCommand['LastAccessTime'] = infoRecord['LastAccessTime']
                     respSMBCommand['LastWriteTime']  = infoRecord['LastWriteTime']
                     respSMBCommand['ChangeTime']     = infoRecord['ChangeTime']
                     respSMBCommand['AllocationSize'] = infoRecord['AllocationSize']
                     respSMBCommand['EndofFile']      = infoRecord['EndOfFile']
                     respSMBCommand['FileAttributes'] = infoRecord['FileAttributes']
                 if errorCode == STATUS_SUCCESS:
                     del(connData['OpenedFiles'][fileID])
        else:
            errorCode = STATUS_INVALID_HANDLE

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2QueryInfo(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb2.SMB2QueryInfo_Response()

        queryInfo = smb2.SMB2QueryInfo(recvPacket['Data'])

        errorCode = STATUS_SUCCESS

        respSMBCommand['OutputBufferOffset'] = 0x48
        respSMBCommand['Buffer'] = '\x00'

        if str(queryInfo['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(queryInfo['FileID'])
        else:
            fileID = str(queryInfo['FileID'])

        if recvPacket['TreeID'] in connData['ConnectedShares']:
            if fileID in connData['OpenedFiles']:
                fileName = connData['OpenedFiles'][fileID]['FileName']

                if queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
                    if queryInfo['FileInfoClass'] == smb2.SMB2_FILE_INTERNAL_INFO:
                        # No need to call queryFileInformation, we have the data here
                        infoRecord = smb2.FileInternalInformation()
                        infoRecord['IndexNumber'] = fileID
                    else:
                        infoRecord, errorCode = queryFileInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass'])
                elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
                    infoRecord = queryFsInformation(os.path.dirname(fileName), os.path.basename(fileName), queryInfo['FileInfoClass'])
                elif queryInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
                    # Failing for now, until we support it
                    infoRecord = None
                    errorCode = STATUS_ACCESS_DENIED
                else:
                    smbServer.log("queryInfo not supported (%x)" %  queryInfo['InfoType'], logging.ERROR)

                if infoRecord is not None:
                    respSMBCommand['OutputBufferLength'] = len(infoRecord)
                    respSMBCommand['Buffer'] = infoRecord
            else:
                errorCode = STATUS_INVALID_HANDLE
        else:
            errorCode = STATUS_SMB_BAD_TID


        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2SetInfo(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand        = smb2.SMB2SetInfo_Response()

        setInfo = smb2.SMB2SetInfo(recvPacket['Data'])

        errorCode = STATUS_SUCCESS

        if str(setInfo['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(setInfo['FileID'])
        else:
            fileID = str(setInfo['FileID'])

        if recvPacket['TreeID'] in connData['ConnectedShares']:
            path     = connData['ConnectedShares'][recvPacket['TreeID']]['path']
            if fileID in connData['OpenedFiles']:
                pathName = connData['OpenedFiles'][fileID]['FileName']

                if setInfo['InfoType'] == smb2.SMB2_0_INFO_FILE:
                    # The file information is being set
                    informationLevel = setInfo['FileInfoClass']
                    if informationLevel == smb2.SMB2_FILE_DISPOSITION_INFO:
                        infoRecord = smb.SMBSetFileDispositionInfo(setInfo['Buffer'])
                        if infoRecord['DeletePending'] > 0:
                           # Mark this file for removal after closed
                           connData['OpenedFiles'][fileID]['DeleteOnClose'] = True
                    elif informationLevel == smb2.SMB2_FILE_BASIC_INFO:
                        infoRecord = smb.SMBSetFileBasicInfo(setInfo['Buffer'])
                        # Creation time won't be set,  the other ones we play with.
                        atime = infoRecord['LastWriteTime']
                        if atime == 0:
                            atime = -1
                        else:
                            atime = getUnixTime(atime)
                        mtime = infoRecord['ChangeTime']
                        if mtime == 0:
                            mtime = -1
                        else:
                            mtime = getUnixTime(mtime)
                        if atime > 0 and mtime > 0:
                            os.utime(pathName,(atime,mtime))
                    elif informationLevel == smb2.SMB2_FILE_END_OF_FILE_INFO:
                        fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
                        infoRecord = smb.SMBSetFileEndOfFileInfo(setInfo['Buffer'])
                        if infoRecord['EndOfFile'] > 0:
                            os.lseek(fileHandle, infoRecord['EndOfFile']-1, 0)
                            os.write(fileHandle, '\x00')
                    elif informationLevel == smb2.SMB2_FILE_RENAME_INFO:
                        renameInfo = smb2.FILE_RENAME_INFORMATION_TYPE_2(setInfo['Buffer'])
                        newPathName = os.path.join(path,renameInfo['FileName'].decode('utf-16le').replace('\\', '/'))
                        if renameInfo['ReplaceIfExists'] == 0 and os.path.exists(newPathName):
                            return [smb2.SMB2Error()], None, STATUS_OBJECT_NAME_COLLISION
                        try:
                             os.rename(pathName,newPathName)
                             connData['OpenedFiles'][fileID]['FileName'] = newPathName
                        except Exception as e:
                             smbServer.log("smb2SetInfo: %s" % e, logging.ERROR)
                             errorCode = STATUS_ACCESS_DENIED
                    else:
                        smbServer.log('Unknown level for set file info! 0x%x' % informationLevel, logging.ERROR)
                        # UNSUPPORTED
                        errorCode =  STATUS_NOT_SUPPORTED
                #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_FILESYSTEM:
                #    # The underlying object store information is being set.
                #    setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
                #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_SECURITY:
                #    # The security information is being set.
                #    # Failing for now, until we support it
                #    infoRecord = None
                #    errorCode = STATUS_ACCESS_DENIED
                #elif setInfo['InfoType'] == smb2.SMB2_0_INFO_QUOTA:
                #    # The underlying object store quota information is being set.
                #    setInfo = queryFsInformation('/', fileName, queryInfo['FileInfoClass'])
                else:
                    smbServer.log("setInfo not supported (%x)" %  setInfo['InfoType'], logging.ERROR)

            else:
                errorCode = STATUS_INVALID_HANDLE
        else:
            errorCode = STATUS_SMB_BAD_TID


        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Write(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Write_Response()
        writeRequest   = smb2.SMB2Write(recvPacket['Data'])

        respSMBCommand['Buffer'] = '\x00'

        if str(writeRequest['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(writeRequest['FileID'])
        else:
            fileID = str(writeRequest['FileID'])

        if fileID in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     offset = writeRequest['Offset']
                     # If we're trying to write past the file end we just skip the write call (Vista does this)
                     if os.lseek(fileHandle, 0, 2) >= offset:
                         os.lseek(fileHandle,offset,0)
                         os.write(fileHandle,writeRequest['Buffer'])
                 else:
                     sock = connData['OpenedFiles'][fileID]['Socket']
                     sock.send(writeRequest['Buffer'])

                 respSMBCommand['Count']    = writeRequest['Length']
                 respSMBCommand['Remaining']= 0xff
             except Exception as e:
                 smbServer.log('SMB2_WRITE: %s' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Read(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Read_Response()
        readRequest   = smb2.SMB2Read(recvPacket['Data'])

        respSMBCommand['Buffer'] = '\x00'

        if str(readRequest['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(readRequest['FileID'])
        else:
            fileID = str(readRequest['FileID'])

        if fileID in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][fileID]['FileHandle']
             errorCode = 0
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     offset = readRequest['Offset']
                     os.lseek(fileHandle,offset,0)
                     content = os.read(fileHandle,readRequest['Length'])
                 else:
                     sock = connData['OpenedFiles'][fileID]['Socket']
                     content = sock.recv(readRequest['Length'])

                 respSMBCommand['DataOffset']   = 0x50
                 respSMBCommand['DataLength']   = len(content)
                 respSMBCommand['DataRemaining']= 0
                 respSMBCommand['Buffer']       = content
             except Exception as e:
                 smbServer.log('SMB2_READ: %s ' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Flush(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Flush_Response()
        flushRequest   = smb2.SMB2Flush(recvPacket['Data'])

        if str(flushRequest['FileID']) in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][str(flushRequest['FileID'])]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 os.fsync(fileHandle)
             except Exception as e:
                 smbServer.log("SMB2_FLUSH %s" % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_HANDLE

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode


    @staticmethod
    def smb2QueryDirectory(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)
        respSMBCommand = smb2.SMB2QueryDirectory_Response()
        queryDirectoryRequest   = smb2.SMB2QueryDirectory(recvPacket['Data'])

        respSMBCommand['Buffer'] = '\x00'

        # The server MUST locate the tree connection, as specified in section 3.3.5.2.11.
        if (recvPacket['TreeID'] in connData['ConnectedShares']) is False:
            return [smb2.SMB2Error()], None, STATUS_NETWORK_NAME_DELETED

        # Next, the server MUST locate the open for the directory to be queried
        # If no open is found, the server MUST fail the request with STATUS_FILE_CLOSED
        if str(queryDirectoryRequest['FileID']) == '\xff'*16:
            # Let's take the data from the lastRequest
            if  'SMB2_CREATE' in connData['LastRequest']:
                fileID = connData['LastRequest']['SMB2_CREATE']['FileID']
            else:
                fileID = str(queryDirectoryRequest['FileID'])
        else:
            fileID = str(queryDirectoryRequest['FileID'])

        if (fileID in connData['OpenedFiles']) is False:
            return [smb2.SMB2Error()], None, STATUS_FILE_CLOSED

        # If the open is not an open to a directory, the request MUST be failed
        # with STATUS_INVALID_PARAMETER.
        if os.path.isdir(connData['OpenedFiles'][fileID]['FileName']) is False:
            return [smb2.SMB2Error()], None, STATUS_INVALID_PARAMETER

        # If any other information class is specified in the FileInformationClass
        # field of the SMB2 QUERY_DIRECTORY Request, the server MUST fail the
        # operation with STATUS_INVALID_INFO_CLASS.
        if queryDirectoryRequest['FileInformationClass'] not in (
        smb2.FILE_DIRECTORY_INFORMATION, smb2.FILE_FULL_DIRECTORY_INFORMATION, smb2.FILEID_FULL_DIRECTORY_INFORMATION,
        smb2.FILE_BOTH_DIRECTORY_INFORMATION, smb2.FILEID_BOTH_DIRECTORY_INFORMATION, smb2.FILENAMES_INFORMATION):
            return [smb2.SMB2Error()], None, STATUS_INVALID_INFO_CLASS

        # If SMB2_REOPEN is set in the Flags field of the SMB2 QUERY_DIRECTORY
        # Request, the server SHOULD<326> set Open.EnumerationLocation to 0
        # and Open.EnumerationSearchPattern to an empty string.
        if queryDirectoryRequest['Flags'] & smb2.SMB2_REOPEN:
            connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0
            connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = ''

        # If SMB2_RESTART_SCANS is set in the Flags field of the SMB2
        # QUERY_DIRECTORY Request, the server MUST set
        # Open.EnumerationLocation to 0.
        if queryDirectoryRequest['Flags'] & smb2.SMB2_RESTART_SCANS:
            connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = 0

        # If Open.EnumerationLocation is 0 and Open.EnumerationSearchPattern
        # is an empty string, then Open.EnumerationSearchPattern MUST be set
        # to the search pattern specified in the SMB2 QUERY_DIRECTORY by
        # FileNameOffset and FileNameLength. If FileNameLength is 0, the server
        # SHOULD<327> set Open.EnumerationSearchPattern as "*" to search all entries.

        pattern = queryDirectoryRequest['Buffer'].decode('utf-16le')
        if  connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0 and \
            connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] == '':
            if pattern == '':
                pattern = '*'
            connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

        # If SMB2_INDEX_SPECIFIED is set and FileNameLength is not zero,
        # the server MUST set Open.EnumerationSearchPattern to the search pattern
        # specified in the request by FileNameOffset and FileNameLength.
        if queryDirectoryRequest['Flags'] & smb2.SMB2_INDEX_SPECIFIED and \
           queryDirectoryRequest['FileNameLength'] > 0:
            connData['OpenedFiles'][fileID]['Open']['EnumerationSearchPattern'] = pattern

        pathName = os.path.join(os.path.normpath(connData['OpenedFiles'][fileID]['FileName']),pattern)
        searchResult, searchCount, errorCode = findFirst2(os.path.dirname(pathName),
                  os.path.basename(pathName),
                  queryDirectoryRequest['FileInformationClass'],
                  smb.ATTR_DIRECTORY, isSMB2 = True )

        if errorCode != STATUS_SUCCESS:
            return [smb2.SMB2Error()], None, errorCode

        if searchCount > 2 and pattern == '*':
            # strip . and ..
            searchCount -= 2
            searchResult = searchResult[2:]

        if searchCount == 0 and connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] == 0:
            return [smb2.SMB2Error()], None, STATUS_NO_SUCH_FILE

        if  connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] < 0:
            return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES

        totalData = 0
        respData = ''
        for nItem in range(connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'], searchCount):
            connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] += 1
            if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
                # If single entry is requested we must clear the NextEntryOffset
                searchResult[nItem]['NextEntryOffset'] = 0
            data = searchResult[nItem].getData()
            lenData = len(data)
            padLen = (8-(lenData % 8)) %8

            if (totalData+lenData) >= queryDirectoryRequest['OutputBufferLength']:
                connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] -= 1
                break
            else:
                respData += data + '\x00'*padLen
                totalData += lenData + padLen

            if queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY:
                break

        if connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] >= searchCount:
             connData['OpenedFiles'][fileID]['Open']['EnumerationLocation'] = -1

        respSMBCommand['OutputBufferOffset'] = 0x48
        respSMBCommand['OutputBufferLength'] = totalData
        respSMBCommand['Buffer'] = respData

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2ChangeNotify(connId, smbServer, recvPacket):

        return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

    @staticmethod
    def smb2Echo(connId, smbServer, recvPacket):

        respSMBCommand = smb2.SMB2Echo_Response()

        return [respSMBCommand], None, STATUS_SUCCESS

    @staticmethod
    def smb2TreeDisconnect(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2TreeDisconnect_Response()

        if recvPacket['TreeID'] in connData['ConnectedShares']:
            smbServer.log("Disconnecting Share(%d:%s)" % (recvPacket['TreeID'],connData['ConnectedShares'][recvPacket['TreeID']]['shareName']))
            del(connData['ConnectedShares'][recvPacket['TreeID']])
            errorCode = STATUS_SUCCESS
        else:
            # STATUS_SMB_BAD_TID
            errorCode = STATUS_SMB_BAD_TID


        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Logoff(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Logoff_Response()

        if recvPacket['SessionID'] != connData['Uid']:
            # STATUS_SMB_BAD_UID
            errorCode = STATUS_SMB_BAD_UID
        else:
            errorCode = STATUS_SUCCESS

        connData['Uid'] = 0

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Ioctl(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Ioctl_Response()
        ioctlRequest   = smb2.SMB2Ioctl(recvPacket['Data'])

        ioctls = smbServer.getIoctls()
        if ioctlRequest['CtlCode'] in ioctls:
            outputData, errorCode = ioctls[ioctlRequest['CtlCode']](connId, smbServer, ioctlRequest)
            if errorCode == STATUS_SUCCESS:
                respSMBCommand['CtlCode']      = ioctlRequest['CtlCode']
                respSMBCommand['FileID']       = ioctlRequest['FileID']
                respSMBCommand['InputOffset']  = 0
                respSMBCommand['InputCount']   = 0
                respSMBCommand['OutputOffset'] = 0x70
                respSMBCommand['OutputCount']  = len(outputData)
                respSMBCommand['Flags']        = 0
                respSMBCommand['Buffer']       = outputData
            else:
                respSMBCommand = outputData
        else:
            smbServer.log("Ioctl not implemented command: 0x%x" % ioctlRequest['CtlCode'],logging.DEBUG)
            errorCode = STATUS_INVALID_DEVICE_REQUEST
            respSMBCommand = smb2.SMB2Error()

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Lock(connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2Lock_Response()

        # I'm actually doing nothing.. just make MacOS happy ;)
        errorCode = STATUS_SUCCESS

        smbServer.setConnectionData(connId, connData)
        return [respSMBCommand], None, errorCode

    @staticmethod
    def smb2Cancel(connId, smbServer, recvPacket):
        # I'm actually doing nothing
        return [smb2.SMB2Error()], None, STATUS_CANCELLED

    @staticmethod
    def default(connId, smbServer, recvPacket):
        # By default we return an SMB Packet with error not implemented
        smbServer.log("Not implemented command: 0x%x" % recvPacket['Command'],logging.DEBUG)
        return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

class Ioctls:
   @staticmethod
   def fsctlDfsGetReferrals(connId, smbServer, ioctlRequest):
        return smb2.SMB2Error(), STATUS_FS_DRIVER_REQUIRED

   @staticmethod
   def fsctlPipeTransceive(connId, smbServer, ioctlRequest):
        connData = smbServer.getConnectionData(connId)

        ioctlResponse = ''

        if str(ioctlRequest['FileID']) in connData['OpenedFiles']:
             fileHandle = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['FileHandle']
             errorCode = STATUS_SUCCESS
             try:
                 if fileHandle != PIPE_FILE_DESCRIPTOR:
                     errorCode = STATUS_INVALID_DEVICE_REQUEST
                 else:
                     sock = connData['OpenedFiles'][str(ioctlRequest['FileID'])]['Socket']
                     sock.sendall(ioctlRequest['Buffer'])
                     ioctlResponse = sock.recv(ioctlRequest['MaxOutputResponse'])
             except Exception as e:
                 smbServer.log('fsctlPipeTransceive: %s ' % e, logging.ERROR)
                 errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_INVALID_DEVICE_REQUEST

        smbServer.setConnectionData(connId, connData)
        return ioctlResponse, errorCode

   @staticmethod
   def fsctlValidateNegotiateInfo(connId, smbServer, ioctlRequest):
        connData = smbServer.getConnectionData(connId)

        errorCode = STATUS_SUCCESS

        validateNegotiateInfo = smb2.VALIDATE_NEGOTIATE_INFO(ioctlRequest['Buffer'])
        validateNegotiateInfo['Capabilities'] = 0
        validateNegotiateInfo['Guid'] = 'A'*16
        validateNegotiateInfo['SecurityMode'] = 1
        validateNegotiateInfo['Dialects'] = (smb2.SMB2_DIALECT_002,)

        smbServer.setConnectionData(connId, connData)
        return validateNegotiateInfo.getData(), errorCode


class SMBSERVERHandler(SocketServer.BaseRequestHandler):
    def __init__(self, request, client_address, server, select_poll = False):
        self.__SMB = server
        self.__ip, self.__port = client_address
        self.__request = request
        self.__connId = threading.currentThread().getName()
        self.__timeOut = 60*5
        self.__select_poll = select_poll
        #self.__connId = os.getpid()
        SocketServer.BaseRequestHandler.__init__(self, request, client_address, server)

    def handle(self):
        self.__SMB.log("Incoming connection (%s,%d)" % (self.__ip, self.__port))
        self.__SMB.addConnection(self.__connId, self.__ip, self.__port)
        while True:
            try:
                # Firt of all let's get the NETBIOS packet
                session = nmb.NetBIOSTCPSession(self.__SMB.getServerName(),'HOST', self.__ip, sess_port = self.__port, sock = self.__request, select_poll = self.__select_poll)
                try:
                    p = session.recv_packet(self.__timeOut)
                except nmb.NetBIOSTimeout:
                    raise
                except nmb.NetBIOSError:
                    break

                if p.get_type() == nmb.NETBIOS_SESSION_REQUEST:
                   # Someone is requesting a session, we're gonna accept them all :)
                   _, rn, my = p.get_trailer().split(' ')
                   remote_name = nmb.decode_name('\x20'+rn)
                   myname = nmb.decode_name('\x20'+my)
                   self.__SMB.log("NetBIOS Session request (%s,%s,%s)" % (self.__ip, remote_name[1].strip(), myname[1]))
                   r = nmb.NetBIOSSessionPacket()
                   r.set_type(nmb.NETBIOS_SESSION_POSITIVE_RESPONSE)
                   r.set_trailer(p.get_trailer())
                   self.__request.send(r.rawData())
                else:
                   resp = self.__SMB.processRequest(self.__connId, p.get_trailer())
                   # Send all the packets recevied. Except for big transactions this should be
                   # a single packet
                   for i in resp:
                       session.send_packet(str(i))
            except Exception as e:
                self.__SMB.log("Handle: %s" % e)
                #import traceback
                #traceback.print_exc()
                break

    def finish(self):
        # Thread/process is dying, we should tell the main SMB thread to remove all this thread data
        self.__SMB.log("Closing down connection (%s,%d)" % (self.__ip, self.__port))
        self.__SMB.removeConnection(self.__connId)
        return SocketServer.BaseRequestHandler.finish(self)

class SMBSERVER(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
#class SMBSERVER(SocketServer.ForkingMixIn, SocketServer.TCPServer):
    def __init__(self, server_address, handler_class=SMBSERVERHandler, config_parser = None):
        SocketServer.TCPServer.allow_reuse_address = True
        SocketServer.TCPServer.__init__(self, server_address, handler_class)

        # Server name and OS to be presented whenever is necessary
        self.__serverName   = ''
        self.__serverOS     = ''
        self.__serverDomain = ''
        self.__challenge    = ''
        self.__log          = None

        # Our ConfigParser data
        self.__serverConfig = config_parser

        # Our credentials to be used during the server's lifetime
        self.__credentials = {}

        # Our log file
        self.__logFile = ''

        # Registered Named Pipes, format is PipeName,Socket
        self.__registeredNamedPipes = {}

        # JTR dump path
        self.__jtr_dump_path = ''

        # SMB2 Support flag = default not active
        self.__SMB2Support = False

        # Our list of commands we will answer, by default the NOT IMPLEMENTED one
        self.__smbCommandsHandler = SMBCommands()
        self.__smbTrans2Handler   = TRANS2Commands()
        self.__smbTransHandler    = TRANSCommands()
        self.__smbNTTransHandler  = NTTRANSCommands()
        self.__smb2CommandsHandler = SMB2Commands()
        self.__IoctlHandler       = Ioctls()

        self.__smbNTTransCommands = {
        # NT IOCTL, can't find doc for this
        0xff                               :self.__smbNTTransHandler.default
        }

        self.__smbTransCommands  = {
'\\PIPE\\LANMAN'                       :self.__smbTransHandler.lanMan,
smb.SMB.TRANS_TRANSACT_NMPIPE          :self.__smbTransHandler.transactNamedPipe,
        }
        self.__smbTrans2Commands = {
 smb.SMB.TRANS2_FIND_FIRST2            :self.__smbTrans2Handler.findFirst2,
 smb.SMB.TRANS2_FIND_NEXT2             :self.__smbTrans2Handler.findNext2,
 smb.SMB.TRANS2_QUERY_FS_INFORMATION   :self.__smbTrans2Handler.queryFsInformation,
 smb.SMB.TRANS2_QUERY_PATH_INFORMATION :self.__smbTrans2Handler.queryPathInformation,
 smb.SMB.TRANS2_QUERY_FILE_INFORMATION :self.__smbTrans2Handler.queryFileInformation,
 smb.SMB.TRANS2_SET_FILE_INFORMATION   :self.__smbTrans2Handler.setFileInformation,
 smb.SMB.TRANS2_SET_PATH_INFORMATION   :self.__smbTrans2Handler.setPathInformation
        }

        self.__smbCommands = {
 #smb.SMB.SMB_COM_FLUSH:              self.__smbCommandsHandler.smbComFlush,
 smb.SMB.SMB_COM_CREATE_DIRECTORY:   self.__smbCommandsHandler.smbComCreateDirectory,
 smb.SMB.SMB_COM_DELETE_DIRECTORY:   self.__smbCommandsHandler.smbComDeleteDirectory,
 smb.SMB.SMB_COM_RENAME:             self.__smbCommandsHandler.smbComRename,
 smb.SMB.SMB_COM_DELETE:             self.__smbCommandsHandler.smbComDelete,
 smb.SMB.SMB_COM_NEGOTIATE:          self.__smbCommandsHandler.smbComNegotiate,
 smb.SMB.SMB_COM_SESSION_SETUP_ANDX: self.__smbCommandsHandler.smbComSessionSetupAndX,
 smb.SMB.SMB_COM_LOGOFF_ANDX:        self.__smbCommandsHandler.smbComLogOffAndX,
 smb.SMB.SMB_COM_TREE_CONNECT_ANDX:  self.__smbCommandsHandler.smbComTreeConnectAndX,
 smb.SMB.SMB_COM_TREE_DISCONNECT:    self.__smbCommandsHandler.smbComTreeDisconnect,
 smb.SMB.SMB_COM_ECHO:               self.__smbCommandsHandler.smbComEcho,
 smb.SMB.SMB_COM_QUERY_INFORMATION:  self.__smbCommandsHandler.smbQueryInformation,
 smb.SMB.SMB_COM_TRANSACTION2:       self.__smbCommandsHandler.smbTransaction2,
 smb.SMB.SMB_COM_TRANSACTION:        self.__smbCommandsHandler.smbTransaction,
 # Not needed for now
 smb.SMB.SMB_COM_NT_TRANSACT:        self.__smbCommandsHandler.smbNTTransact,
 smb.SMB.SMB_COM_QUERY_INFORMATION_DISK: self.__smbCommandsHandler.smbQueryInformationDisk,
 smb.SMB.SMB_COM_OPEN_ANDX:          self.__smbCommandsHandler.smbComOpenAndX,
 smb.SMB.SMB_COM_QUERY_INFORMATION2: self.__smbCommandsHandler.smbComQueryInformation2,
 smb.SMB.SMB_COM_READ_ANDX:          self.__smbCommandsHandler.smbComReadAndX,
 smb.SMB.SMB_COM_READ:               self.__smbCommandsHandler.smbComRead,
 smb.SMB.SMB_COM_WRITE_ANDX:         self.__smbCommandsHandler.smbComWriteAndX,
 smb.SMB.SMB_COM_WRITE:              self.__smbCommandsHandler.smbComWrite,
 smb.SMB.SMB_COM_CLOSE:              self.__smbCommandsHandler.smbComClose,
 smb.SMB.SMB_COM_LOCKING_ANDX:       self.__smbCommandsHandler.smbComLockingAndX,
 smb.SMB.SMB_COM_NT_CREATE_ANDX:     self.__smbCommandsHandler.smbComNtCreateAndX,
 0xFF:                               self.__smbCommandsHandler.default
}

        self.__smb2Ioctls = {
 smb2.FSCTL_DFS_GET_REFERRALS:            self.__IoctlHandler.fsctlDfsGetReferrals,
# smb2.FSCTL_PIPE_PEEK:                    self.__IoctlHandler.fsctlPipePeek,
# smb2.FSCTL_PIPE_WAIT:                    self.__IoctlHandler.fsctlPipeWait,
 smb2.FSCTL_PIPE_TRANSCEIVE:              self.__IoctlHandler.fsctlPipeTransceive,
# smb2.FSCTL_SRV_COPYCHUNK:                self.__IoctlHandler.fsctlSrvCopyChunk,
# smb2.FSCTL_SRV_ENUMERATE_SNAPSHOTS:      self.__IoctlHandler.fsctlSrvEnumerateSnapshots,
# smb2.FSCTL_SRV_REQUEST_RESUME_KEY:       self.__IoctlHandler.fsctlSrvRequestResumeKey,
# smb2.FSCTL_SRV_READ_HASH:                self.__IoctlHandler.fsctlSrvReadHash,
# smb2.FSCTL_SRV_COPYCHUNK_WRITE:          self.__IoctlHandler.fsctlSrvCopyChunkWrite,
# smb2.FSCTL_LMR_REQUEST_RESILIENCY:       self.__IoctlHandler.fsctlLmrRequestResiliency,
# smb2.FSCTL_QUERY_NETWORK_INTERFACE_INFO: self.__IoctlHandler.fsctlQueryNetworkInterfaceInfo,
# smb2.FSCTL_SET_REPARSE_POINT:            self.__IoctlHandler.fsctlSetReparsePoint,
# smb2.FSCTL_DFS_GET_REFERRALS_EX:         self.__IoctlHandler.fsctlDfsGetReferralsEx,
# smb2.FSCTL_FILE_LEVEL_TRIM:              self.__IoctlHandler.fsctlFileLevelTrim,
 smb2.FSCTL_VALIDATE_NEGOTIATE_INFO:      self.__IoctlHandler.fsctlValidateNegotiateInfo,
}

        self.__smb2Commands = {
 smb2.SMB2_NEGOTIATE:       self.__smb2CommandsHandler.smb2Negotiate,
 smb2.SMB2_SESSION_SETUP:   self.__smb2CommandsHandler.smb2SessionSetup,
 smb2.SMB2_LOGOFF:          self.__smb2CommandsHandler.smb2Logoff,
 smb2.SMB2_TREE_CONNECT:    self.__smb2CommandsHandler.smb2TreeConnect,
 smb2.SMB2_TREE_DISCONNECT: self.__smb2CommandsHandler.smb2TreeDisconnect,
 smb2.SMB2_CREATE:          self.__smb2CommandsHandler.smb2Create,
 smb2.SMB2_CLOSE:           self.__smb2CommandsHandler.smb2Close,
 smb2.SMB2_FLUSH:           self.__smb2CommandsHandler.smb2Flush,
 smb2.SMB2_READ:            self.__smb2CommandsHandler.smb2Read,
 smb2.SMB2_WRITE:           self.__smb2CommandsHandler.smb2Write,
 smb2.SMB2_LOCK:            self.__smb2CommandsHandler.smb2Lock,
 smb2.SMB2_IOCTL:           self.__smb2CommandsHandler.smb2Ioctl,
 smb2.SMB2_CANCEL:          self.__smb2CommandsHandler.smb2Cancel,
 smb2.SMB2_ECHO:            self.__smb2CommandsHandler.smb2Echo,
 smb2.SMB2_QUERY_DIRECTORY: self.__smb2CommandsHandler.smb2QueryDirectory,
 smb2.SMB2_CHANGE_NOTIFY:   self.__smb2CommandsHandler.smb2ChangeNotify,
 smb2.SMB2_QUERY_INFO:      self.__smb2CommandsHandler.smb2QueryInfo,
 smb2.SMB2_SET_INFO:        self.__smb2CommandsHandler.smb2SetInfo,
# smb2.SMB2_OPLOCK_BREAK:    self.__smb2CommandsHandler.smb2SessionSetup,
 0xFF:                      self.__smb2CommandsHandler.default
}

        # List of active connections
        self.__activeConnections = {}

    def getIoctls(self):
        return self.__smb2Ioctls

    def getCredentials(self):
        return self.__credentials

    def removeConnection(self, name):
        try:
           del(self.__activeConnections[name])
        except:
           pass
        self.log("Remaining connections %s" % self.__activeConnections.keys())

    def addConnection(self, name, ip, port):
        self.__activeConnections[name] = {}
        # Let's init with some know stuff we will need to have
        # TODO: Document what's in there
        #print "Current Connections", self.__activeConnections.keys()
        self.__activeConnections[name]['PacketNum']       = 0
        self.__activeConnections[name]['ClientIP']        = ip
        self.__activeConnections[name]['ClientPort']      = port
        self.__activeConnections[name]['Uid']             = 0
        self.__activeConnections[name]['ConnectedShares'] = {}
        self.__activeConnections[name]['OpenedFiles']     = {}
        # SID results for findfirst2
        self.__activeConnections[name]['SIDs']            = {}
        self.__activeConnections[name]['LastRequest']     = {}

    def getActiveConnections(self):
        return self.__activeConnections

    def setConnectionData(self, connId, data):
        self.__activeConnections[connId] = data
        #print "setConnectionData"
        #print self.__activeConnections

    def getConnectionData(self, connId, checkStatus = True):
        conn = self.__activeConnections[connId]
        if checkStatus is True:
            if ('Authenticated' in conn) is not True:
                # Can't keep going further
                raise Exception("User not Authenticated!")
        return conn

    def getRegisteredNamedPipes(self):
        return self.__registeredNamedPipes

    def registerNamedPipe(self, pipeName, address):
        self.__registeredNamedPipes[unicode(pipeName)] = address
        return True

    def unregisterNamedPipe(self, pipeName):
        if pipeName in self.__registeredNamedPipes:
            del(self.__registeredNamedPipes[unicode(pipeName)])
            return True
        return False

    def unregisterTransaction(self, transCommand):
        if transCommand in self.__smbTransCommands:
           del(self.__smbTransCommands[transCommand])

    def hookTransaction(self, transCommand, callback):
        # If you call this function, callback will replace
        # the current Transaction sub command.
        # (don't get confused with the Transaction smbCommand)
        # If the transaction sub command doesn't not exist, it is added
        # If the transaction sub command exists, it returns the original function         # replaced
        #
        # callback MUST be declared as:
        # callback(connId, smbServer, recvPacket, parameters, data, maxDataCount=0)
        #
        # WHERE:
        #
        # connId      : the connection Id, used to grab/update information about
        #               the current connection
        # smbServer   : the SMBServer instance available for you to ask
        #               configuration data
        # recvPacket  : the full SMBPacket that triggered this command
        # parameters  : the transaction parameters
        # data        : the transaction data
        # maxDataCount: the max amount of data that can be transfered agreed
        #               with the client
        #
        # and MUST return:
        # respSetup, respParameters, respData, errorCode
        #
        # WHERE:
        #
        # respSetup: the setup response of the transaction
        # respParameters: the parameters response of the transaction
        # respData: the data reponse of the transaction
        # errorCode: the NT error code

        if transCommand in self.__smbTransCommands:
           originalCommand = self.__smbTransCommands[transCommand]
        else:
           originalCommand = None

        self.__smbTransCommands[transCommand] = callback
        return originalCommand

    def unregisterTransaction2(self, transCommand):
        if transCommand in self.__smbTrans2Commands:
           del(self.__smbTrans2Commands[transCommand])

    def hookTransaction2(self, transCommand, callback):
        # Here we should add to __smbTrans2Commands
        # Same description as Transaction
        if transCommand in self.__smbTrans2Commands:
           originalCommand = self.__smbTrans2Commands[transCommand]
        else:
           originalCommand = None

        self.__smbTrans2Commands[transCommand] = callback
        return originalCommand

    def unregisterNTTransaction(self, transCommand):
        if transCommand in self.__smbNTTransCommands:
           del(self.__smbNTTransCommands[transCommand])

    def hookNTTransaction(self, transCommand, callback):
        # Here we should add to __smbNTTransCommands
        # Same description as Transaction
        if transCommand in self.__smbNTTransCommands:
           originalCommand = self.__smbNTTransCommands[transCommand]
        else:
           originalCommand = None

        self.__smbNTTransCommands[transCommand] = callback
        return originalCommand

    def unregisterSmbCommand(self, smbCommand):
        if smbCommand in self.__smbCommands:
           del(self.__smbCommands[smbCommand])

    def hookSmbCommand(self, smbCommand, callback):
        # Here we should add to self.__smbCommands
        # If you call this function, callback will replace
        # the current smbCommand.
        # If smbCommand doesn't not exist, it is added
        # If SMB command exists, it returns the original function replaced
        #
        # callback MUST be declared as:
        # callback(connId, smbServer, SMBCommand, recvPacket)
        #
        # WHERE:
        #
        # connId    : the connection Id, used to grab/update information about
        #             the current connection
        # smbServer : the SMBServer instance available for you to ask
        #             configuration data
        # SMBCommand: the SMBCommand itself, with its data and parameters.
        #             Check smb.py:SMBCommand() for a reference
        # recvPacket: the full SMBPacket that triggered this command
        #
        # and MUST return:
        # <list of respSMBCommands>, <list of packets>, errorCode
        # <list of packets> has higher preference over commands, in case you
        # want to change the whole packet
        # errorCode: the NT error code
        #
        # For SMB_COM_TRANSACTION2, SMB_COM_TRANSACTION and SMB_COM_NT_TRANSACT
        # the callback function is slightly different:
        #
        # callback(connId, smbServer, SMBCommand, recvPacket, transCommands)
        #
        # WHERE:
        #
        # transCommands: a list of transaction subcommands already registered
        #

        if smbCommand in self.__smbCommands:
           originalCommand = self.__smbCommands[smbCommand]
        else:
           originalCommand = None

        self.__smbCommands[smbCommand] = callback
        return originalCommand

    def unregisterSmb2Command(self, smb2Command):
        if smb2Command in self.__smb2Commands:
           del(self.__smb2Commands[smb2Command])

    def hookSmb2Command(self, smb2Command, callback):
        if smb2Command in self.__smb2Commands:
           originalCommand = self.__smb2Commands[smb2Command]
        else:
           originalCommand = None

        self.__smb2Commands[smb2Command] = callback
        return originalCommand

    def log(self, msg, level=logging.INFO):
        self.__log.log(level,msg)

    def getServerName(self):
        return self.__serverName

    def getServerOS(self):
        return self.__serverOS

    def getServerDomain(self):
        return self.__serverDomain

    def getSMBChallenge(self):
        return self.__challenge

    def getServerConfig(self):
        return self.__serverConfig

    def setServerConfig(self, config):
        self.__serverConfig = config

    def getJTRdumpPath(self):
        return self.__jtr_dump_path

    def verify_request(self, request, client_address):
        # TODO: Control here the max amount of processes we want to launch
        # returning False, closes the connection
        return True

    def processRequest(self, connId, data):

        # TODO: Process batched commands.
        isSMB2      = False
        SMBCommand  = None
        try:
            packet = smb.NewSMBPacket(data = data)
            SMBCommand  = smb.SMBCommand(packet['Data'][0])
        except:
            # Maybe a SMB2 packet?
            packet = smb2.SMB2Packet(data = data)
            isSMB2 = True

        # We might have compound requests
        compoundedPacketsResponse = []
        compoundedPackets         = []
        try:
            # Search out list of implemented commands
            # We provide them with:
            # connId      : representing the data for this specific connection
            # self        : the SMBSERVER if they want to ask data to it
            # SMBCommand  : the SMBCommand they are expecting to process
            # packet      : the received packet itself, in case they need more data than the actual command
            # Only for Transactions
            # transCommand: a list of transaction subcommands
            # We expect to get:
            # respCommands: a list of answers for the commands processed
            # respPacket  : if the commands chose to directly craft packet/s, we use this and not the previous
            #               this MUST be a list
            # errorCode   : self explanatory
            if isSMB2 is False:
                if packet['Command'] == smb.SMB.SMB_COM_TRANSACTION2:
                    respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                  connId,
                                  self,
                                  SMBCommand,
                                  packet,
                                  self.__smbTrans2Commands)
                elif packet['Command'] == smb.SMB.SMB_COM_NT_TRANSACT:
                    respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                  connId,
                                  self,
                                  SMBCommand,
                                  packet,
                                  self.__smbNTTransCommands)
                elif packet['Command'] == smb.SMB.SMB_COM_TRANSACTION:
                    respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                  connId,
                                  self,
                                  SMBCommand,
                                  packet,
                                  self.__smbTransCommands)
                else:
                    if packet['Command'] in self.__smbCommands:
                       if self.__SMB2Support is True:
                           if packet['Command'] == smb.SMB.SMB_COM_NEGOTIATE:
                               try:
                                   respCommands, respPackets, errorCode = self.__smb2Commands[smb2.SMB2_NEGOTIATE](connId, self, packet, True)
                                   isSMB2 = True
                               except Exception as e:
                                   self.log('SMB2_NEGOTIATE: %s' % e, logging.ERROR)
                                   # If something went wrong, let's fallback to SMB1
                                   respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                       connId,
                                       self,
                                       SMBCommand,
                                       packet)
                                   #self.__SMB2Support = False
                                   pass
                           else:
                               respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                       connId,
                                       self,
                                       SMBCommand,
                                       packet)
                       else:
                           respCommands, respPackets, errorCode = self.__smbCommands[packet['Command']](
                                       connId,
                                       self,
                                       SMBCommand,
                                       packet)
                    else:
                       respCommands, respPackets, errorCode = self.__smbCommands[255](connId, self, SMBCommand, packet)

                compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
                compoundedPackets.append(packet)

            else:
                done = False
                while not done:
                    if packet['Command'] in self.__smb2Commands:
                       if self.__SMB2Support is True:
                           respCommands, respPackets, errorCode = self.__smb2Commands[packet['Command']](
                                   connId,
                                   self,
                                   packet)
                       else:
                           respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
                    else:
                       respCommands, respPackets, errorCode = self.__smb2Commands[255](connId, self, packet)
                    # Let's store the result for this compounded packet
                    compoundedPacketsResponse.append((respCommands, respPackets, errorCode))
                    compoundedPackets.append(packet)
                    if packet['NextCommand'] != 0:
                        data = data[packet['NextCommand']:]
                        packet = smb2.SMB2Packet(data = data)
                    else:
                        done = True

        except Exception as e:
            #import traceback
            #traceback.print_exc()
            # Something wen't wrong, defaulting to Bad user ID
            self.log('processRequest (0x%x,%s)' % (packet['Command'],e), logging.ERROR)
            raise

        # We prepare the response packet to commands don't need to bother about that.
        connData    = self.getConnectionData(connId, False)

        # Force reconnection loop.. This is just a test.. client will send me back credentials :)
        #connData['PacketNum'] += 1
        #if connData['PacketNum'] == 15:
        #    connData['PacketNum'] = 0
        #    # Something wen't wrong, defaulting to Bad user ID
        #    self.log('Sending BAD USER ID!', logging.ERROR)
        #    #raise
        #    packet['Flags1'] |= smb.SMB.FLAGS1_REPLY
        #    packet['Flags2'] = 0
        #    errorCode = STATUS_SMB_BAD_UID
        #    packet['ErrorCode']   = errorCode >> 16
        #    packet['ErrorClass']  = errorCode & 0xff
        #    return [packet]

        self.setConnectionData(connId, connData)

        packetsToSend = []
        for packetNum in range(len(compoundedPacketsResponse)):
            respCommands, respPackets, errorCode = compoundedPacketsResponse[packetNum]
            packet = compoundedPackets[packetNum]
            if respPackets is None:
                for respCommand in respCommands:
                    if isSMB2 is False:
                        respPacket           = smb.NewSMBPacket()
                        respPacket['Flags1'] = smb.SMB.FLAGS1_REPLY

                        # TODO this should come from a per session configuration
                        respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | packet['Flags2'] & smb.SMB.FLAGS2_UNICODE
                        #respPacket['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES
                        #respPacket['Flags1'] = 0x98
                        #respPacket['Flags2'] = 0xc807


                        respPacket['Tid']    = packet['Tid']
                        respPacket['Mid']    = packet['Mid']
                        respPacket['Pid']    = packet['Pid']
                        respPacket['Uid']    = connData['Uid']

                        respPacket['ErrorCode']   = errorCode >> 16
                        respPacket['_reserved']   = errorCode >> 8 & 0xff
                        respPacket['ErrorClass']  = errorCode & 0xff
                        respPacket.addCommand(respCommand)

                        packetsToSend.append(respPacket)
                    else:
                        respPacket = smb2.SMB2Packet()
                        respPacket['Flags']     = smb2.SMB2_FLAGS_SERVER_TO_REDIR
                        if packetNum > 0:
                            respPacket['Flags'] |= smb2.SMB2_FLAGS_RELATED_OPERATIONS
                        respPacket['Status']    = errorCode
                        respPacket['CreditRequestResponse'] = packet['CreditRequestResponse']
                        respPacket['Command']   = packet['Command']
                        respPacket['CreditCharge'] = packet['CreditCharge']
                        #respPacket['CreditCharge'] = 0
                        respPacket['Reserved']  = packet['Reserved']
                        respPacket['SessionID'] = connData['Uid']
                        respPacket['MessageID'] = packet['MessageID']
                        respPacket['TreeID']    = packet['TreeID']
                        respPacket['Data']      = str(respCommand)
                        packetsToSend.append(respPacket)
            else:
                # The SMBCommand took care of building the packet
                packetsToSend = respPackets

        if isSMB2 is True:
            # Let's build a compound answer
            finalData = ''
            i = 0
            for i in range(len(packetsToSend)-1):
                packet = packetsToSend[i]
                # Align to 8-bytes
                padLen = (8 - (len(packet) % 8) ) % 8
                packet['NextCommand'] = len(packet) + padLen
                finalData += str(packet) + padLen*'\x00'

            # Last one
            finalData += str(packetsToSend[len(packetsToSend)-1])
            packetsToSend = [finalData]

        # We clear the compound requests
        connData['LastRequest'] = {}

        return packetsToSend

    def processConfigFile(self, configFile = None):
        # TODO: Do a real config parser
        if self.__serverConfig is None:
            if configFile is None:
                configFile = 'smb.conf'
            self.__serverConfig = configparser.ConfigParser()
            self.__serverConfig.read(configFile)

        self.__serverName   = self.__serverConfig.get('global','server_name')
        self.__serverOS     = self.__serverConfig.get('global','server_os')
        self.__serverDomain = self.__serverConfig.get('global','server_domain')
        self.__logFile      = self.__serverConfig.get('global','log_file')
        if self.__serverConfig.has_option('global', 'challenge'):
            self.__challenge    = self.__serverConfig.get('global', 'challenge')
        else:
            self.__challenge    = 'A'*8

        if self.__serverConfig.has_option("global", "jtr_dump_path"):
            self.__jtr_dump_path = self.__serverConfig.get("global", "jtr_dump_path")

        if self.__serverConfig.has_option("global", "SMB2Support"):
            self.__SMB2Support = self.__serverConfig.getboolean("global","SMB2Support")
        else:
            self.__SMB2Support = False

        if self.__logFile != 'None':
            logging.basicConfig(filename = self.__logFile,
                             level = logging.DEBUG,
                             format="%(asctime)s: %(levelname)s: %(message)s",
                             datefmt = '%m/%d/%Y %I:%M:%S %p')
        self.__log        = LOG

        # Process the credentials
        credentials_fname = self.__serverConfig.get('global','credentials_file')
        if credentials_fname is not "":
            cred = open(credentials_fname)
            line = cred.readline()
            while line:
                name, domain, lmhash, nthash = line.split(':')
                self.__credentials[name] = (domain, lmhash, nthash.strip('\r\n'))
                line = cred.readline()
            cred.close()
        self.log('Config file parsed')

# For windows platforms, opening a directory is not an option, so we set a void FD
VOID_FILE_DESCRIPTOR = -1
PIPE_FILE_DESCRIPTOR = -2
