ffmpeg中samba网络协议的兼容分析(三)

之前我们发现回调函数my_smbc_get_auth_data_with_context_fn内能获取到用户名和工作组等,并且都是我设置的值,在这个回调中打个断点看看,我们再次调试

image

看到左侧的调用栈,红框中的几个函数都是libsmbclient的源码内的,我们查找samba源码(4.0.26版本):

int smbc_open(const char *furl,
          int flags,
          mode_t mode)
{
    SMBCFILE * file;
    int fd;

        file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode);
    if (!file)
        return -1;

    fd = add_fd(file);
    if (fd == -1) 
                smbc_getFunctionClose(statcont)(statcont, file);
    return fd;
}

smbc_open_fn smbc_getFunctionOpen(SMBCCTX *c)
{
        return c->open;
}

这里的smbc_open_fn其实是这样的:

typedef SMBCFILE * (*smbc_open_fn)(SMBCCTX *c,
                                   const char *fname,
                                   int flags,
                                   mode_t mode);

smbc_open_fn smbc_getFunctionOpen(SMBCCTX *c);

void smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn);

而SMBCCTX为

typedef struct _SMBCCTX SMBCCTX;

struct _SMBCCTX
{
        /**
         * debug level
         *
         * DEPRECATED:
         * Use smbc_getDebug() and smbc_setDebug()
         */
        int     debug DEPRECATED_SMBC_INTERFACE;
    
        /**
         * netbios name used for making connections
         *
         * DEPRECATED:
         * Use smbc_getNetbiosName() and smbc_setNetbiosName()
         */
        char * netbios_name DEPRECATED_SMBC_INTERFACE;

        /**
         * workgroup name used for making connections
         *
         * DEPRECATED:
         * Use smbc_getWorkgroup() and smbc_setWorkgroup()
         */
        char * workgroup DEPRECATED_SMBC_INTERFACE;

        /**
         * username used for making connections
         *
         * DEPRECATED:
         * Use smbc_getUser() and smbc_setUser()
         */
        char * user DEPRECATED_SMBC_INTERFACE;

        /**
         * timeout used for waiting on connections / response data (in
         * milliseconds)
         *
         * DEPRECATED:
         * Use smbc_getTimeout() and smbc_setTimeout()
         */
        int timeout DEPRECATED_SMBC_INTERFACE;

    /**
         * callable functions for files:
     * For usage and return values see the SMBC_* functions
         *
         * DEPRECATED:
         *
         * Use smbc_getFunction*() and smbc_setFunction*(), e.g.
         * smbc_getFunctionOpen(), smbc_setFunctionUnlink(), etc.
     */ 
        smbc_open_fn                    open DEPRECATED_SMBC_INTERFACE;
        smbc_creat_fn                   creat DEPRECATED_SMBC_INTERFACE;
        smbc_read_fn                    read DEPRECATED_SMBC_INTERFACE;
        smbc_write_fn                   write DEPRECATED_SMBC_INTERFACE;
        smbc_unlink_fn                  unlink DEPRECATED_SMBC_INTERFACE;
        smbc_rename_fn                  rename DEPRECATED_SMBC_INTERFACE;
        smbc_lseek_fn                   lseek DEPRECATED_SMBC_INTERFACE;
        smbc_stat_fn                    stat DEPRECATED_SMBC_INTERFACE;
        smbc_fstat_fn                   fstat DEPRECATED_SMBC_INTERFACE;
#if 0 /* internal */
        smbc_ftruncate_fn               ftruncate_fn;
#endif
        smbc_close_fn                   close_fn DEPRECATED_SMBC_INTERFACE;
        smbc_opendir_fn                 opendir DEPRECATED_SMBC_INTERFACE;
        smbc_closedir_fn                closedir DEPRECATED_SMBC_INTERFACE;
        smbc_readdir_fn                 readdir DEPRECATED_SMBC_INTERFACE;
        smbc_getdents_fn                getdents DEPRECATED_SMBC_INTERFACE;
        smbc_mkdir_fn                   mkdir DEPRECATED_SMBC_INTERFACE;
        smbc_rmdir_fn                   rmdir DEPRECATED_SMBC_INTERFACE;
        smbc_telldir_fn                 telldir DEPRECATED_SMBC_INTERFACE;
        smbc_lseekdir_fn                lseekdir DEPRECATED_SMBC_INTERFACE;
        smbc_fstatdir_fn                fstatdir DEPRECATED_SMBC_INTERFACE;
        smbc_chmod_fn                   chmod DEPRECATED_SMBC_INTERFACE;
        smbc_utimes_fn                  utimes DEPRECATED_SMBC_INTERFACE;
        smbc_setxattr_fn                setxattr DEPRECATED_SMBC_INTERFACE;
        smbc_getxattr_fn                getxattr DEPRECATED_SMBC_INTERFACE;
        smbc_removexattr_fn             removexattr DEPRECATED_SMBC_INTERFACE;
        smbc_listxattr_fn               listxattr DEPRECATED_SMBC_INTERFACE;

        /* Printing-related functions */
        smbc_print_file_fn              print_file DEPRECATED_SMBC_INTERFACE;
        smbc_open_print_job_fn          open_print_job DEPRECATED_SMBC_INTERFACE;
        smbc_list_print_jobs_fn         list_print_jobs DEPRECATED_SMBC_INTERFACE;
        smbc_unlink_print_job_fn        unlink_print_job DEPRECATED_SMBC_INTERFACE;

        /*
        ** Callbacks
        *
        * DEPRECATED:
        *
        * See the comment above each field, for the getter and setter
        * functions that should now be used.
        */
    struct _smbc_callbacks
        {
        /**
                 * authentication function callback: called upon auth requests
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionAuthData(), smbc_setFunctionAuthData()
         */
                smbc_get_auth_data_fn auth_fn DEPRECATED_SMBC_INTERFACE;
        
        /**
                 * check if a server is still good
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionCheckServer(),
                 * smbc_setFunctionCheckServer()
         */
        smbc_check_server_fn check_server_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * remove a server if unused
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionRemoveUnusedServer(),
                 * smbc_setFunctionCheckServer()
         */
        smbc_remove_unused_server_fn remove_unused_server_fn DEPRECATED_SMBC_INTERFACE;

        /** Cache subsystem
                 *
         * For an example cache system see
         * samba/source/libsmb/libsmb_cache.c
                 *
                 * Cache subsystem * functions follow.
         */

        /**
                 * server cache addition 
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionAddCachedServer(),
                 * smbc_setFunctionAddCachedServer()
         */
        smbc_add_cached_srv_fn add_cached_srv_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * server cache lookup 
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionGetCachedServer(),
                 * smbc_setFunctionGetCachedServer()
         */
        smbc_get_cached_srv_fn get_cached_srv_fn DEPRECATED_SMBC_INTERFACE;

        /**
                 * server cache removal
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionRemoveCachedServer(),
                 * smbc_setFunctionRemoveCachedServer()
         */
        smbc_remove_cached_srv_fn remove_cached_srv_fn DEPRECATED_SMBC_INTERFACE;
        
        /**
                 * server cache purging, try to remove all cached servers
                 * (disconnect)
                 *
                 * DEPRECATED:
                 * Use smbc_getFunctionPurgeCachedServers(),
                 * smbc_setFunctionPurgeCachedServers()
         */
        smbc_purge_cached_fn purge_cached_fn DEPRECATED_SMBC_INTERFACE;
    } callbacks;

        /**
         * Space where the private data of the server cache used to be
         *
         * DEPRECATED:
         * Use smbc_getServerCacheData(), smbc_setServerCacheData()
         */
        void * reserved DEPRECATED_SMBC_INTERFACE;

        /*
         * Very old configuration options.
         * 
         * DEPRECATED:
         * Use one of the following functions instead:
         *   smbc_setOptionUseKerberos()
         *   smbc_getOptionUseKerberos()
         *   smbc_setOptionFallbackAfterKerberos()
         *   smbc_getOptionFallbackAfterKerberos()
         *   smbc_setOptionNoAutoAnonymousLogin()
         *   smbc_getOptionNoAutoAnonymousLogin()
         */
        int flags DEPRECATED_SMBC_INTERFACE;
    
        /**
         * user options selections that apply to this session
         *
         * NEW OPTIONS ARE NOT ADDED HERE!
         *
         * DEPRECATED:
         * To set and retrieve options, use the smbc_setOption*() and
         * smbc_getOption*() functions.
         */
        struct _smbc_options {
                int browse_max_lmb_count DEPRECATED_SMBC_INTERFACE;
                int urlencode_readdir_entries DEPRECATED_SMBC_INTERFACE;
                int one_share_per_server DEPRECATED_SMBC_INTERFACE;
        } options DEPRECATED_SMBC_INTERFACE;
    
    /** INTERNAL DATA
     * do _NOT_ touch this from your program !
     */
    struct SMBC_internal_data * internal;
};

通过上面代码看出c->open,_SMBCCTX的open的类型就是smbc_open_fn

而statcont是一个SMBCCTX*的全局变量,在这里初始化了

static SMBCCTX * statcont = NULL;

int smbc_init(smbc_get_auth_data_fn fn,
          int debug)
{
    if (!smbc_compat_initialized) {
        statcont = smbc_new_context();
        if (!statcont) 
            return -1;

                smbc_setDebug(statcont, debug);
                smbc_setFunctionAuthData(statcont, fn);

        if (!smbc_init_context(statcont)) {
            smbc_free_context(statcont, False);
            return -1;
        }

        smbc_compat_initialized = 1;

        return 0;
    }
    return 0;
}

所以smbc_getFunctionOpen(statcont)() == statcont->open()

所以,file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode)实质就是执行了smbc_open_fn()

在smbc_open_fn申明的下方有个这个:

void smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn);

源码为:

void
smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn)
{
        c->open = fn;
}

从这个smbc_setFunctionOpen可以推断,此函数是为SMBCCTX的open成员赋值用的

果不其然,在smbc_new_context里的确设置了这一open成员

/*
 * Get a new empty handle to fill in with your own info
 */
SMBCCTX *
smbc_new_context(void)
{
        SMBCCTX *context;
    TALLOC_CTX *frame = talloc_stackframe();

        /* The first call to this function should initialize the module */
        SMB_THREAD_ONCE(&SMBC_initialized, SMBC_module_init, NULL);

        /*
         * All newly added context fields should be placed in
         * SMBC_internal_data, not directly in SMBCCTX.
         */
        context = SMB_MALLOC_P(SMBCCTX);
        if (!context) {
        TALLOC_FREE(frame);
                errno = ENOMEM;
                return NULL;
        }

        ZERO_STRUCTP(context);

        context->internal = SMB_MALLOC_P(struct SMBC_internal_data);
        if (!context->internal) {
        TALLOC_FREE(frame);
                SAFE_FREE(context);
                errno = ENOMEM;
                return NULL;
        }

        /* Initialize the context and establish reasonable defaults */
        ZERO_STRUCTP(context->internal);

        smbc_setDebug(context, 0);
        smbc_setTimeout(context, 20000);

        smbc_setOptionFullTimeNames(context, False);
        smbc_setOptionOpenShareMode(context, SMBC_SHAREMODE_DENY_NONE);
        smbc_setOptionSmbEncryptionLevel(context, SMBC_ENCRYPTLEVEL_NONE);
        smbc_setOptionUseCCache(context, True);
        smbc_setOptionCaseSensitive(context, False);
        smbc_setOptionBrowseMaxLmbCount(context, 3);    /* # LMBs to query */
        smbc_setOptionUrlEncodeReaddirEntries(context, False);
        smbc_setOptionOneSharePerServer(context, False);
    if (getenv("LIBSMBCLIENT_NO_CCACHE") == NULL) {
        smbc_setOptionUseCCache(context, true);
    }

        smbc_setFunctionAuthData(context, SMBC_get_auth_data);
        smbc_setFunctionCheckServer(context, SMBC_check_server);
        smbc_setFunctionRemoveUnusedServer(context, SMBC_remove_unused_server);

        smbc_setOptionUserData(context, NULL);
        smbc_setFunctionAddCachedServer(context, SMBC_add_cached_server);
        smbc_setFunctionGetCachedServer(context, SMBC_get_cached_server);
        smbc_setFunctionRemoveCachedServer(context, SMBC_remove_cached_server);
        smbc_setFunctionPurgeCachedServers(context, SMBC_purge_cached_servers);

        smbc_setFunctionOpen(context, SMBC_open_ctx);
        smbc_setFunctionCreat(context, SMBC_creat_ctx);
        smbc_setFunctionRead(context, SMBC_read_ctx);
        smbc_setFunctionWrite(context, SMBC_write_ctx);
        smbc_setFunctionClose(context, SMBC_close_ctx);
        smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
        smbc_setFunctionRename(context, SMBC_rename_ctx);
        smbc_setFunctionLseek(context, SMBC_lseek_ctx);
        smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx);
        smbc_setFunctionStat(context, SMBC_stat_ctx);
        smbc_setFunctionStatVFS(context, SMBC_statvfs_ctx);
        smbc_setFunctionFstatVFS(context, SMBC_fstatvfs_ctx);
        smbc_setFunctionFstat(context, SMBC_fstat_ctx);
        smbc_setFunctionOpendir(context, SMBC_opendir_ctx);
        smbc_setFunctionClosedir(context, SMBC_closedir_ctx);
        smbc_setFunctionReaddir(context, SMBC_readdir_ctx);
        smbc_setFunctionGetdents(context, SMBC_getdents_ctx);
        smbc_setFunctionMkdir(context, SMBC_mkdir_ctx);
        smbc_setFunctionRmdir(context, SMBC_rmdir_ctx);
        smbc_setFunctionTelldir(context, SMBC_telldir_ctx);
        smbc_setFunctionLseekdir(context, SMBC_lseekdir_ctx);
        smbc_setFunctionFstatdir(context, SMBC_fstatdir_ctx);
        smbc_setFunctionChmod(context, SMBC_chmod_ctx);
        smbc_setFunctionUtimes(context, SMBC_utimes_ctx);
        smbc_setFunctionSetxattr(context, SMBC_setxattr_ctx);
        smbc_setFunctionGetxattr(context, SMBC_getxattr_ctx);
        smbc_setFunctionRemovexattr(context, SMBC_removexattr_ctx);
        smbc_setFunctionListxattr(context, SMBC_listxattr_ctx);

        smbc_setFunctionOpenPrintJob(context, SMBC_open_print_job_ctx);
        smbc_setFunctionPrintFile(context, SMBC_print_file_ctx);
        smbc_setFunctionListPrintJobs(context, SMBC_list_print_jobs_ctx);
        smbc_setFunctionUnlinkPrintJob(context, SMBC_unlink_print_job_ctx);

    TALLOC_FREE(frame);
        return context;
}

通过下面这句代码,我们可以定位到SMBC_open_ctx

smbc_setFunctionOpen(context, SMBC_open_ctx);

SMBC_open_ctx,正是红色方框调用栈中的其中一个小伙伴

我们来看下它的源码:

SMBCFILE * SMBC_open_ctx(SMBCCTX *context,
              const char *fname,
              int flags,
              mode_t mode)
{
    char *server = NULL;
        char *share = NULL;
        char *user = NULL;
        char *password = NULL;
        char *workgroup = NULL;
    char *path = NULL;
    char *targetpath = NULL;
    struct cli_state *targetcli = NULL;
    SMBCSRV *srv   = NULL;
    SMBCFILE *file = NULL;
    uint16_t fd;
    NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
    TALLOC_CTX *frame = talloc_stackframe();

    if (!context || !context->internal->initialized) {
        errno = EINVAL;  /* Best I can think of ... */
        TALLOC_FREE(frame);
        return NULL;
    }

    if (!fname) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
    }

    if (SMBC_parse_path(frame,
                            context,
                            fname,
                            &workgroup,
                            &server,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
        }

    if (!user || user[0] == (char)0) {
        user = talloc_strdup(frame, smbc_getUser(context));
        if (!user) {
                    errno = ENOMEM;
            TALLOC_FREE(frame);
            return NULL;
        }
    }

    srv = SMBC_server(frame, context, True,
                          server, share, &workgroup, &user, &password);
    if (!srv) {
        if (errno == EPERM) errno = EACCES;
        TALLOC_FREE(frame);
        return NULL;  /* SMBC_server sets errno */
    }

    /* Hmmm, the test for a directory is suspect here ... FIXME */

    if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
        status = NT_STATUS_OBJECT_PATH_INVALID;
    } else {
        file = SMB_MALLOC_P(SMBCFILE);
        if (!file) {
            errno = ENOMEM;
            TALLOC_FREE(frame);
            return NULL;
        }

        ZERO_STRUCTP(file);

        /*d_printf(">>>open: resolving %s\n", path);*/
        status = cli_resolve_path(
            frame, "", context->internal->auth_info,
            srv->cli, path, &targetcli, &targetpath);
        if (!NT_STATUS_IS_OK(status)) {
            d_printf("Could not resolve %s\n", path);
                        errno = ENOENT;
            SAFE_FREE(file);
            TALLOC_FREE(frame);
            return NULL;
        }
        /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/

        status = cli_open(targetcli, targetpath, flags,
                                   context->internal->share_mode, &fd);
        if (!NT_STATUS_IS_OK(status)) {

            /* Handle the error ... */

            SAFE_FREE(file);
            errno = SMBC_errno(context, targetcli);
            TALLOC_FREE(frame);
            return NULL;
        }

        /* Fill in file struct */

        file->cli_fd  = fd;
        file->fname   = SMB_STRDUP(fname);
        file->srv     = srv;
        file->offset  = 0;
        file->file    = True;

        DLIST_ADD(context->internal->files, file);

                /*
                 * If the file was opened in O_APPEND mode, all write
                 * operations should be appended to the file.  To do that,
                 * though, using this protocol, would require a getattrE()
                 * call for each and every write, to determine where the end
                 * of the file is. (There does not appear to be an append flag
                 * in the protocol.)  Rather than add all of that overhead of
                 * retrieving the current end-of-file offset prior to each
                 * write operation, we'll assume that most append operations
                 * will continuously write, so we'll just set the offset to
                 * the end of the file now and hope that's adequate.
                 *
                 * Note to self: If this proves inadequate, and O_APPEND
                 * should, in some cases, be forced for each write, add a
                 * field in the context options structure, for
                 * "strict_append_mode" which would select between the current
                 * behavior (if FALSE) or issuing a getattrE() prior to each
                 * write and forcing the write to the end of the file (if
                 * TRUE).  Adding that capability will likely require adding
                 * an "append" flag into the _SMBCFILE structure to track
                 * whether a file was opened in O_APPEND mode.  -- djl
                 */
                if (flags & O_APPEND) {
                        if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
                                (void) SMBC_close_ctx(context, file);
                                errno = ENXIO;
                TALLOC_FREE(frame);
                                return NULL;
                        }
                }

        TALLOC_FREE(frame);
        return file;
    }

    /* Check if opendir needed ... */

    if (!NT_STATUS_IS_OK(status)) {
        int eno = 0;

        eno = SMBC_errno(context, srv->cli);
        file = smbc_getFunctionOpendir(context)(context, fname);
        if (!file) errno = eno;
        TALLOC_FREE(frame);
        return file;
    }

    errno = EINVAL; /* FIXME, correct errno ? */
    TALLOC_FREE(frame);
    return NULL;
}

从函数的参数类型和返回类型就可以断定,这正是我们所希望的SMBC_open_ctx(符合预期)

再次看到红框中调用栈,的确是smbc_open_ctx调用了smbc_server,也符合代码中写得那样

srv = SMBC_server(frame, context, True,
                          server, share, &workgroup, &user, &password);

我们先找到smbc_server的源码

SMBCSRV *
SMBC_server(TALLOC_CTX *ctx,
        SMBCCTX *context,
        bool connect_if_not_found,
        const char *server,
        const char *share,
        char **pp_workgroup,
        char **pp_username,
        char **pp_password)
{
    SMBCSRV *srv=NULL;
    bool in_cache = false;

    srv = SMBC_server_internal(ctx, context, connect_if_not_found,
            server, share, pp_workgroup,
            pp_username, pp_password, &in_cache);

    if (!srv) {
        return NULL;
    }
    if (in_cache) {
        return srv;
    }

    /* Now add it to the cache (internal or external)  */
    /* Let the cache function set errno if it wants to */
    errno = 0;
    if (smbc_getFunctionAddCachedServer(context)(context, srv,
                        server, share,
                        *pp_workgroup,
                        *pp_username)) {
        int saved_errno = errno;
        DEBUG(3, (" Failed to add server to cache\n"));
        errno = saved_errno;
        if (errno == 0) {
            errno = ENOMEM;
        }
        SAFE_FREE(srv);
        return NULL;
    }

    DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
        server, share, srv));

    DLIST_ADD(context->internal->servers, srv);
    return srv;
}

发现此函数又一次符合预期(红框)的调用了SMBC_server_internal函数,此时,内心会问:这里的password和username等直接就从smbc_server传入了SMBC_server_internal,那么password和username等信息是不是在SMBC_server_internal里面才处理的呢?

为了方便阅读,我就不一一去贴出下面将要调用的SMBC_find_server的代码了,直接说答案:并没有!

其实是在SMBC_parse_path进行处理的(smbc_open_ctx中的)

if (SMBC_parse_path(frame,
                            context,
                            fname,
                            &workgroup,
                            &server,
                            &share,
                            &path,
                            &user,
                            &password,
                            NULL)) {
        errno = EINVAL;
        TALLOC_FREE(frame);
        return NULL;
        }

SMBC_parse_path的代码如下:

#define SMBC_PREFIX "smb:"

int SMBC_parse_path(TALLOC_CTX *ctx,
        SMBCCTX *context,
                const char *fname,
                char **pp_workgroup,
                char **pp_server,
                char **pp_share,
                char **pp_path,
        char **pp_user,
                char **pp_password,
                char **pp_options)
{
    char *s;
    const char *p;
    char *q, *r;
    char *workgroup = NULL;
    int len;

    /* Ensure these returns are at least valid pointers. */
    *pp_server = talloc_strdup(ctx, "");
    *pp_share = talloc_strdup(ctx, "");
    *pp_path = talloc_strdup(ctx, "");
    *pp_user = talloc_strdup(ctx, "");
    *pp_password = talloc_strdup(ctx, "");

    if (!*pp_server || !*pp_share || !*pp_path ||
            !*pp_user || !*pp_password) {
        return -1;
    }

        /*
         * Assume we wont find an authentication domain to parse, so default
         * to the workgroup in the provided context.
         */
    if (pp_workgroup != NULL) {
        *pp_workgroup =
                        talloc_strdup(ctx, smbc_getWorkgroup(context));
    }

    if (pp_options) {
        *pp_options = talloc_strdup(ctx, "");
    }
    s = talloc_strdup(ctx, fname);

    /* see if it has the right prefix */
    len = strlen(SMBC_PREFIX);
    if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
                return -1; /* What about no smb: ? */
        }

    p = s + len;

    /* Watch the test below, we are testing to see if we should exit */

    if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
                DEBUG(1, ("Invalid path (does not begin with smb://"));
        return -1;
    }

    p += 2;  /* Skip the double slash */

        /* See if any options were specified */
        if ((q = strrchr(p, '?')) != NULL ) {
                /* There are options.  Null terminate here and point to them */
                *q++ = '\0';

                DEBUG(4, ("Found options '%s'", q));

        /* Copy the options */
        if (pp_options && *pp_options != NULL) {
            TALLOC_FREE(*pp_options);
            *pp_options = talloc_strdup(ctx, q);
        }
    }

    if (*p == '\0') {
        goto decoding;
    }

    if (*p == '/') {
        int wl = strlen(smbc_getWorkgroup(context));

        if (wl > 16) {
            wl = 16;
        }

        *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context));
        if (!*pp_server) {
            return -1;
        }
        (*pp_server)[wl] = '\0';
        return 0;
    }

    /*
     * ok, its for us. Now parse out the server, share etc.
     *
     * However, we want to parse out [[domain;]user[:password]@] if it
     * exists ...
     */

    /* check that '@' occurs before '/', if '/' exists at all */
    q = strchr_m(p, '@');
    r = strchr_m(p, '/');
    if (q && (!r || q < r)) {
        char *userinfo = NULL;
        const char *u;

        next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
        if (!userinfo) {
            return -1;
        }
        u = userinfo;

        if (strchr_m(u, ';')) {
            next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
            if (!workgroup) {
                return -1;
            }
            if (pp_workgroup) {
                *pp_workgroup = workgroup;
            }
        }

        if (strchr_m(u, ':')) {
            next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
            if (!*pp_user) {
                return -1;
            }
            *pp_password = talloc_strdup(ctx, u);
            if (!*pp_password) {
                return -1;
            }
        } else {
            *pp_user = talloc_strdup(ctx, u);
            if (!*pp_user) {
                return -1;
            }
        }
    }

    if (!next_token_talloc(ctx, &p, pp_server, "/")) {
        return -1;
    }

    if (*p == (char)0) {
        goto decoding;  /* That's it ... */
    }

    if (!next_token_talloc(ctx, &p, pp_share, "/")) {
        return -1;
    }

        /*
         * Prepend a leading slash if there's a file path, as required by
         * NetApp filers.
         */
        if (*p != '\0') {
        *pp_path = talloc_asprintf(ctx,
                                           "\\%s",
                                           p);
        } else {
        *pp_path = talloc_strdup(ctx, "");
    }
    if (!*pp_path) {
        return -1;
    }
    string_replace(*pp_path, '/', '\\');

decoding:
    (void) urldecode_talloc(ctx, pp_path, *pp_path);
    (void) urldecode_talloc(ctx, pp_server, *pp_server);
    (void) urldecode_talloc(ctx, pp_share, *pp_share);
    (void) urldecode_talloc(ctx, pp_user, *pp_user);
    (void) urldecode_talloc(ctx, pp_password, *pp_password);

    if (!workgroup) {
        workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
    }
    if (!workgroup) {
        return -1;
    }

    /* set the credentials to make DFS work */
    smbc_set_credentials_with_fallback(context,
                           workgroup,
                           *pp_user,
                           *pp_password);
    return 0;
}

粗略阅读,发现这个函数就是对url进行字符串处理的

其中包括url中的“@”和“/”等

(我当时很傻,通过此段代码来反推smb链接的传参规则)

我们直接看到上方的注释(因为在定义与注释之间有个宏吸引了我:#define SMBC_PREFIX "smb:")


/*
 * Function to parse a path and turn it into components
 *
 * The general format of an SMB URI is explain in Christopher Hertel's CIFS
 * book, at http://ubiqx.org/cifs/Appendix-D.html.  We accept a subset of the
 * general format ("smb:" only; we do not look for "cifs:").
 *
 *
 * We accept:
 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
 *
 * Meaning of URLs:
 *
 * smb://           Show all workgroups.
 *
 *                  The method of locating the list of workgroups varies
 *                  depending upon the setting of the context variable
 *                  context->options.browse_max_lmb_count.  This value
 *                  determines the maximum number of local master browsers to
 *                  query for the list of workgroups.  In order to ensure that
 *                  a complete list of workgroups is obtained, all master
 *                  browsers must be queried, but if there are many
 *                  workgroups, the time spent querying can begin to add up.
 *                  For small networks (not many workgroups), it is suggested
 *                  that this variable be set to 0, indicating query all local
 *                  master browsers.  When the network has many workgroups, a
 *                  reasonable setting for this variable might be around 3.
 *
 * smb://name/      if name<1D> or name<1B> exists, list servers in
 *                  workgroup, else, if name<20> exists, list all shares
 *                  for server ...
 *
 * If "options" are provided, this function returns the entire option list as a
 * string, for later parsing by the caller.  Note that currently, no options
 * are supported.
 */

#define SMBC_PREFIX "smb:"

在注释中,我么终于找到了答案

/*
....
* We accept:
 *  smb://[[[domain;]user[:password]@]server[/share[/path[/file]]]][?options]
 ...
*/

你可能感兴趣的:(ffmpeg中samba网络协议的兼容分析(三))