# 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
import 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
