Question

I'm using the pysmb library to query shares/directory structures on SMB/CIFS network shares.

def ListShares(Server, Username=None, Password=None, Domain=None):
    Ip = socket.gethostbyname(Server)
    conn = SMBConnection(Username,
                         Password,
                         'MyApp',
                         Server,
                         Domain,
                         use_ntlm_v2=True,
                         sign_options=SMBConnection.SIGN_WHEN_SUPPORTED,
                         is_direct_tcp=True)
    assert conn.connect(Ip)

    Response = conn.listShares(timeout=30)

    return [{'Name': x.name,
             'Type': x.type,
             'IsTemporary': x.isTemporary,
             'Comments': x.comments} for x in Response if not x.isSpecial]

When connecting to a linux box running samba, I can connect okay and everything works. When I try to connect to a Win7/SBS2008/Server2008 share, I get an error.

If is_direct_tcp=True, I get an Invalid protocol header for Direct TCP session message

File ".../MyApp/Managers/SmbHelper.py", line 38, in ListShares assert conn.connect(Ip) 
File "/opt/pyenv/lib/python3.3/site-packages/smb/SMBConnection.py", line 111, in connect self._pollForNetBIOSPacket(timeout) 
File "/opt/pyenv/lib/python3.3/site-packages/smb/SMBConnection.py", line 504, in _pollForNetBIOSPacket self.feedData(data) 
File "/opt/pyenv/lib/python3.3/site-packages/nmb/base.py", line 49, in feedData length = self.data_nmb.decode(self.data_buf, offset) 
File "/opt/pyenv/lib/python3.3/site-packages/nmb/nmb_structs.py", line 60, in decode raise NMBError("Invalid protocol header for Direct TCP session message") 

If is_direct_tcp=False, I get a NotConnectedError

File ".../MyApp/Managers/SmbHelper.py", line 38, in ListShares assert conn.connect(Ip) 
File "/opt/pyenv/lib/python3.3/lib/site-packages/smb/SMBConnection.py", line 111, in connect self._pollForNetBIOSPacket(timeout) 
File "/opt/pyenv/lib/python3.3/lib/site-packages/smb/SMBConnection.py", line 466, in _pollForNetBIOSPacket raise NotConnectedError 

I'm hitting a bit of a brick wall. How can I work out what exactly is wrong and fix it?

Further diagnostics...

smbclient -L linux.domain.local   -U MyUsername -W domain //Works
smbclient -L linux.domain.local   -U MyUsername@domain    //Doesn't work (Auth failed)
smbclient -L windows.domain.local -U MyUsername -W domain //Doesn't work (Auth failed)
smbclient -L windows.domain.local -U MyUsername@domain    //Works

smbclient -L [either].domain.local -U MyUsername@domain -W domain //Works, despite redundancy

So it seems Linux gets the domain from the -W parameter, Windows gets it from the Username@Domain syntax and providing both makes the smbclient call succeed to either server. Unfortunately, connecting to Windows doesn't succeed succeed from within pysmb even if I use the @Domain syntax

Solution

There were 3 problems... Firstly, when use_direct_tcp = True, port needs to be 445. When it's False, port should be 139. There was also a bug when using the module from Python3 (bytes were being incorrectly encoded). Finally, there was a problem with the way it was communicating with the server (at least when connecting to Windows boxes rather than a linux samba server).

Michael Teo, author of the module has developed a fix which we've tested and works. He's planning to update the package shortly.

Was it helpful?

Solution

I'm not sure if this helps in your case, but it works for me:

  1. The 3rd parameter to the SmbConnection should be (I think) the client_machine_name, so I pass there what I get from socket.gethostname().

  2. I'm not using the sign_options and is_direct_tcp I just leave the default values.

This works for me with both samba and windows shares (I just have to pass a different port number sometimes).

Here is the code I use:

class Smb(object):
    def __init__(self, username, password, server, share, port=139):
        # split username if it contains a domain (domain\username)
        domain, username = username.split('\\') if username.count('\\') == 1 else ('', username)
        # setup data
        self.domain    = str(domain)
        self.username  = str(username)
        self.password  = str(password)
        self.client    = socket.gethostname()
        self.server    = str(server)
        self.server_ip = socket.gethostbyname(server)
        self.share     = str(share)
        self.port      = port
        self.conn      = None
        self.connected = False
        # SMB.SMBConnection logs too much
        smb_logger = logging.getLogger('SMB.SMBConnection')
        smb_logger.setLevel(logging.WARNING)

    def connect(self):
        try:
            self.conn = SMBConnection(self.username, self.password,
                                      self.client, self.server,
                                      use_ntlm_v2=True, domain=self.domain)
            self.connected = self.conn.connect(self.server_ip, self.port)
            logger.info('Connected to %s' % self.server)
            return self.connected
        except Exception, e:
            logger.error('Connect failed. Reason: %s', e)
            return False

And use it as:

smb = Smb('domain\\user', 'password', 'server', 'share_name')
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top