Search code examples
pythonpysmb

pysmb from linux to Windows, Unable to connect to shared device


Trying to connect to an smb share via pysmb and getting error...

smb.smb_structs.OperationFailure: Failed to list  on \\\\H021BSBD20\\shared_folder: Unable to connect to shared device

The code I am using looks like...

from smb.SMBConnection import SMBConnection
import json
import pprint
import warnings

pp = pprint.PrettyPrinter(indent=4)
PROJECT_HOME = "/path/to/my/project/"

# load configs
CONF = json.load(open(f"{PROJECT_HOME}/configs/configs.json"))
pp.pprint(CONF)

# list all files in storage smb dir
#https://pysmb.readthedocs.io/en/latest/api/smb_SMBConnection.html#smb.SMBConnection.SMBConnection.listPath
IS_DIRECT_TCP = False
CNXN_PORT = 139 if not IS_DIRECT_TCP else 445
LOCAL_IP = "172.18.4.69"
REMOTE_NAME = "H021BSBD20"  # exact name shown as Device Name in System Settings
SERVICE_NAME = "\\\\H021BSBD20\\shared_folder"
REMOTE_IP = "172.18.7.102"
try:
    conn = SMBConnection(CONF['smb_creds']['username'], CONF['smb_creds']['password'],
                         my_name=LOCAL_IP, remote_name=REMOTE_NAME,
                         use_ntlm_v2=True,
                         is_direct_tcp=IS_DIRECT_TCP)
    conn.connect(REMOTE_IP, CNXN_PORT)
except Exception:
    warnings.warn("\n\nFailed to initially connect, attempting again with param use_ntlm_v2=False\n\n")
    conn = SMBConnection(CONF['smb_creds']['username'], CONF['smb_creds']['password'],
                         my_name=LOCAL_IP, remote_name=REMOTE_NAME,
                         use_ntlm_v2=False,
                         is_direct_tcp=IS_DIRECT_TCP)
    conn.connect(REMOTE_IP, CNXN_PORT)

files = conn.listPath(f'{SERVICE_NAME}', '\\')
pp.pprint(files)

Using smbclient on my machine, I can successfully connect to the share by doing...

[root@airflowetl etl]# smbclient -U my_user \\\\H021BSBD20\\shared_folder

The amount of backslashes I use in the python code is so that I can create the same string that works when using this smbclient (have tried with less backslashes in the code and that has not helped).

Note that the user that I am using the access the shared folder in the python code and with smbclient is not able to access / log on to the actual machine that the share is hosted on (they are only allowed to access that particular shared folder as shown above).

Does anyone know what could be happening here? Any other debugging steps that could be done?


Solution

  • After asking on the github repo Issues section (https://github.com/miketeo/pysmb/issues/169), I was able to fix the problem. It was just due to the arg I was using for the conn.listPath() servicename param.

    When looking closer at the docs for that function (https://pysmb.readthedocs.io/en/latest/api/smb_SMBConnection.html), I saw...

    service_name (string/unicode) – the name of the shared folder for the path

    Originally, I was only looking at the function signature, which said service_name, so I assumed it would be the same as with the smbclient command-line tool (which I have been entering the servicename param as \\\\devicename\\sharename (unlike with pysmb which we can see from the docstring wants just the share as the service_name)).

    So rather than

    files = conn.listPath("\\\\H021BSBD20\\shared_folder", '\\')
    

    I do

    files = conn.listPath("shared_folder", '\\')
    

    The full refactored snippet is shown below, just for reference.

    import argparse
    import json
    import os
    import pprint
    import socket
    import sys
    import traceback
    import warnings
    
    from smb.SMBConnection import SMBConnection
    
    
    def parseArguments():
        # Create argument parser
        parser = argparse.ArgumentParser()
        # Positional mandatory arguments
        parser.add_argument("project_home", help="project home path", type=str)
        parser.add_argument("device_name", help="device (eg. NetBIOS) name in configs of share to process", type=str)
        # Optional arguments
        # parser.add_argument("-dfd", "--data_file_dir",
        #                     help="path to data files dir to be pushed to sink, else source columns based on form_type",
        #                     type=str, default=None)
        # Parse arguments
        args = parser.parse_args()
        return args
    
    
    args = parseArguments()
    for a in args.__dict__:
        print(str(a) + ": " + str(args.__dict__[a]))
    
    pp = pprint.PrettyPrinter(indent=4)
    PROJECT_HOME = args.project_home
    REMOTE_NAME = args.device_name
    
    # load configs
    CONF = json.load(open(f"{PROJECT_HOME}/configs/configs.json"))
    CREDS = json.load(open(f"{PROJECT_HOME}/configs/creds.json"))
    pp.pprint(CONF)
    
    SMB_CONFS = next(info for info in CONF["smb_server_configs"] if info["device_name"] == args.device_name)
    print("\nUsing details for device:")
    pp.pprint(SMB_CONFS)
    
    # list all files in storage smb dir
    #https://pysmb.readthedocs.io/en/latest/api/smb_SMBConnection.html#smb.SMBConnection.SMBConnection.listPath
    IS_DIRECT_TCP = False
    CNXN_PORT = 139 if IS_DIRECT_TCP is False else 445
    LOCAL_IP = socket.gethostname()  #"172.18.4.69"
    REMOTE_NAME = SMB_CONFS["device_name"]
    SHARE_FOLDER = SMB_CONFS["share_folder"]
    REMOTE_IP = socket.gethostbyname(REMOTE_NAME)  # "172.18.7.102"
    print(LOCAL_IP)
    print(REMOTE_NAME)
    try:
        conn = SMBConnection(CREDS['smb_creds']['username'], CREDS['smb_creds']['password'],
                             my_name=LOCAL_IP, remote_name=REMOTE_NAME,
                             use_ntlm_v2=False,
                             is_direct_tcp=IS_DIRECT_TCP)
        conn.connect(REMOTE_IP, CNXN_PORT)
    except Exception:
        traceback.print_exc()
        warnings.warn("\n\nFailed to initially connect, attempting again with param use_ntlm_v2=True\n\n")
        conn = SMBConnection(CREDS['smb_creds']['username'], CREDS['smb_creds']['password'],
                             my_name=LOCAL_IP, remote_name=REMOTE_NAME,
                             use_ntlm_v2=True,
                             is_direct_tcp=IS_DIRECT_TCP)
        conn.connect(REMOTE_IP, CNXN_PORT)
    
    files = conn.listPath(SHARE_FOLDER, '\\')
    if len(files) > 0:
        print("Found listed files")
        for f in files:
            print(f.filename)
    else:
        print("No files to list, this likely indicates a problem. Exiting...")
        exit(255)