Commit Derrell Lipman's changes and fixes to libsmbclient. The build but
authorRichard Sharpe <sharpe@samba.org>
Thu, 18 Mar 2004 17:11:52 +0000 (17:11 +0000)
committerRichard Sharpe <sharpe@samba.org>
Thu, 18 Mar 2004 17:11:52 +0000 (17:11 +0000)
I have not tested them beyond building. I did fix the two instances of
safe_strxxx that were introduced.
(This used to be commit bf89adbcb4c77792004e4ea9da248b71b72b7dbb)

source3/lib/util_sock.c
source3/libsmb/cliconnect.c
source3/libsmb/clientgen.c
source3/libsmb/libsmb_cache.c
source3/libsmb/libsmbclient.c
source3/rpc_client/cli_pipe.c

index 15ce883b099baa59d4d6946d2cd7f444d754abd1..845aaa4b13acc506aea57fb58d82da3ee48f7bdd 100644 (file)
@@ -714,7 +714,7 @@ int open_socket_out(int type, struct in_addr *addr, int port ,int timeout)
        /* create a socket to write to */
        res = socket(PF_INET, type, 0);
        if (res == -1) {
-               DEBUG(0,("socket error\n"));
+                DEBUG(0,("socket error (%s)\n", strerror(errno)));
                return -1;
        }
 
index b5993cd1e66e42e6dd55a944d4a143ecff33004b..938d6fd29bc89370dff5d377dc0b21bd1d713801 100644 (file)
@@ -1595,8 +1595,66 @@ struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip,
        return NULL;
 }
 
-/* Return the IP address and workgroup of a master browser on the 
-   network. */
+/*
+ * Given the IP address of a master browser on the network, return its
+ * workgroup and connect to it.
+ *
+ * This function is provided to allow additional processing beyond what
+ * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master
+ * browsers and obtain each master browsers' list of domains (in case the
+ * first master browser is recently on the network and has not yet
+ * synchronized with other master browsers and therefore does not yet have the
+ * entire network browse list)
+ */
+
+struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info)
+{
+        static fstring name;
+        struct cli_state *cli;
+        struct in_addr server_ip; 
+
+        DEBUG(99, ("Looking up name of master browser %s\n",
+                   inet_ntoa(mb_ip->ip)));
+
+        /*
+         * Do a name status query to find out the name of the master browser.
+         * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain
+         * master browser will not respond to a wildcard query (or, at least,
+         * an NT4 server acting as the domain master browser will not).
+         *
+         * We might be able to use ONLY the query on MSBROWSE, but that's not
+         * yet been tested with all Windows versions, so until it is, leave
+         * the original wildcard query as the first choice and fall back to
+         * MSBROWSE if the wildcard query fails.
+         */
+        if (!name_status_find("*", 0, 0x1d, mb_ip->ip, name) &&
+            !name_status_find(MSBROWSE, 1, 0x1d, mb_ip->ip, name)) {
+
+                DEBUG(99, ("Could not retrieve name status for %s\n",
+                           inet_ntoa(mb_ip->ip)));
+                return NULL;
+        }
+
+        if (!find_master_ip(name, &server_ip)) {
+                DEBUG(99, ("Could not find master ip for %s\n", name));
+                return NULL;
+        }
+
+        pstrcpy(workgroup, name);
+
+        DEBUG(4, ("found master browser %s, %s\n", 
+                  name, inet_ntoa(mb_ip->ip)));
+
+        cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
+
+        return cli;
+    
+}
+
+/*
+ * Return the IP address and workgroup of a master browser on the network, and
+ * connect to it.
+ */
 
 struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user_auth_info *user_info)
 {
@@ -1605,32 +1663,21 @@ struct cli_state *get_ipc_connect_master_ip_bcast(pstring workgroup, struct user
        int i, count;
        struct in_addr server_ip; 
 
+        DEBUG(99, ("Do broadcast lookup for workgroups on local network\n"));
+
         /* Go looking for workgroups by broadcasting on the local network */ 
 
         if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+                DEBUG(99, ("No master browsers responded\n"));
                 return False;
         }
 
        for (i = 0; i < count; i++) {
-               static fstring name;
-
-               if (!name_status_find("*", 0, 0x1d, ip_list[i].ip, name))
-                       continue;
+            DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
 
-                if (!find_master_ip(name, &server_ip))
-                       continue;
-
-                pstrcpy(workgroup, name);
-
-                DEBUG(4, ("found master browser %s, %s\n", 
-                          name, inet_ntoa(ip_list[i].ip)));
-
-               cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info);
-
-               if (!cli)
-                   continue;
-               
-               return cli;
+            cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info);
+            if (cli)
+                    return(cli);
        }
 
        return NULL;
index 20a048388199f46f9f9020ce78c8038aba9f54d6..28da73894fe485d4bc4f124501f7261c5e10ef48 100644 (file)
@@ -367,6 +367,16 @@ void cli_nt_netlogon_netsec_session_close(struct cli_state *cli)
 
 void cli_close_connection(struct cli_state *cli)
 {
+        /*
+         * tell our peer to free his resources.  Wihtout this, when an
+         * application attempts to do a graceful shutdown and calls
+         * smbc_free_context() to clean up all connections, some connections
+         * can remain active on the peer end, until some (long) timeout period
+         * later.  This tree disconnect forces the peer to clean up, since the
+         * connection will be going away.
+         */
+        cli_tdis(cli);
+        
        cli_nt_session_close(cli);
        cli_nt_netlogon_netsec_session_close(cli);
 
index 67dc686b4871ddbef5902dc7abbff8b47ca70775..cb40b4aaa6b5c75c19539a90fe6b5978dae993f4 100644 (file)
@@ -159,10 +159,15 @@ static int smbc_remove_cached_server(SMBCCTX * context, SMBCSRV * server)
  */
 static int smbc_purge_cached(SMBCCTX * context)
 {
-       struct smbc_server_cache * srv = NULL;
+       struct smbc_server_cache * srv;
+       struct smbc_server_cache * next;
        int could_not_purge_all = 0;
 
-       for (srv=((struct smbc_server_cache *) context->server_cache);srv;srv=srv->next) {
+       for (srv = ((struct smbc_server_cache *) context->server_cache),
+                 next = (srv ? srv->next :NULL);
+             srv;
+             srv = next, next = (srv ? srv->next : NULL)) {
+
                if (smbc_remove_unused_server(context, srv->server)) {
                        /* could not be removed */
                        could_not_purge_all = 1;
index 35e8a9786b134b64e78ef1e56227768063c649f0..68bb6661ebdd6041a0c2ede5d2cf10b676db46c6 100644 (file)
@@ -156,18 +156,63 @@ decode_urlpart(char *segment, size_t sizeof_segment)
 /*
  * Function to parse a path and turn it into components
  *
- * We accept smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]]
- * 
- * smb://       means show all the workgroups
- * smb://name/  means, if name<1D> or name<1B> exists, list servers in workgroup,
- *              else, if name<20> exists, list all shares for server ...
+ * 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:"), and expand on
+ * what he calls "context", herein called "options" to avoid conflict with the
+ * SMBCCTX context used throughout this library.  We add the "mb" keyword
+ * which applies as follows:
+ *
+ *
+ * We accept:
+ *  smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]][?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb://           show all workgroups known by the first master browser found
+ * smb://?mb=.any   same as smb:// (i.e. without any options)
+ *
+ * smb://?mb=.all   show all workgroups known by every master browser found.
+ *                  Why might you want this?  In an "appliance" application
+ *                  where the workgroup/domain being used on the local network
+ *                  is not known ahead of time, but where one wanted to
+ *                  provide network services via samba, a unique workgroup
+ *                  could be used.  However, when the appliance is first
+ *                  started, the local samba instance's master browser has not
+ *                  synchronized with the other master browser(s) on the
+ *                  network (and might not synchronize for 12 minutes) and
+ *                  therefore is not aware of the workgroup/ domain names
+ *                  available on the network.  This option may be used to
+ *                  overcome the problem of a libsmbclient application
+ *                  arbitrarily selecting the local (still ignorant) master
+ *                  browser to obtain its list of workgroups/domains and
+ *                  getting back a practically emmpty list.  By requesting
+ *                  the list of workgroups/domains from each found master
+ *                  browser on the local network, a complete list of
+ *                  workgroups/domains can be built.
+ *
+ * smb://?mb=name   NOT YET IMPLEMENTED -- show all workgroups known by the
+ *                  master browser whose name is "name"
+ *
+ * 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.
  */
 
 static const char *smbc_prefix = "smb:";
 
 static int
-smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share, char *path,
-               char *user, char *password) /* FIXME, lengths of strings */
+smbc_parse_path(SMBCCTX *context,
+                const char *fname,
+                char *server, int server_len,
+                char *share, int share_len,
+                char *path, int path_len,
+               char *user, int user_len,
+                char *password, int password_len,
+                char *options, int options_len)
 {
        static pstring s;
        pstring userinfo;
@@ -176,10 +221,11 @@ smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share,
        int len;
 
        server[0] = share[0] = path[0] = user[0] = password[0] = (char)0;
+        if (options != NULL && options_len > 0) {
+                options[0] = (char)0;
+        }
        pstrcpy(s, fname);
 
-       /*  clean_fname(s);  causing problems ... */
-
        /* see if it has the right prefix */
        len = strlen(smbc_prefix);
        if (strncmp(s,smbc_prefix,len) || (s[len] != '/' && s[len] != 0)) {
@@ -192,11 +238,25 @@ smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share,
 
        if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
 
+                DEBUG(1, ("Invalid path (does not begin with smb://"));
                return -1;
 
        }
 
-       p += 2;  /* Skip the // or \\  */
+       p += 2;  /* Skip the double slash */
+
+        /* See if any options were specified */
+        if (q = strrchr(p, '?')) {
+                /* There are options.  Null terminate here and point to them */
+                *q++ = '\0';
+                
+                DEBUG(4, ("Found options '%s'", q));
+
+                /* Copy the options */
+                if (options != NULL && options_len > 0) {
+                        safe_strcpy(options, q, options_len - 1);
+                }
+        }
 
        if (*p == (char)0)
            goto decoding;
@@ -247,10 +307,10 @@ smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share,
                }
 
                if (username[0])
-                       strncpy(user, username, sizeof(fstring));  /* FIXME, size and domain */
+                       strncpy(user, username, user_len);  /* FIXME, domain */
 
                if (passwd[0])
-                       strncpy(password, passwd, sizeof(fstring)); /* FIXME, size */
+                       strncpy(password, passwd, password_len);
 
        }
 
@@ -268,24 +328,57 @@ smbc_parse_path(SMBCCTX *context, const char *fname, char *server, char *share,
 
        }
 
-       pstrcpy(path, p);
+        safe_strcpy(path, p, path_len - 1);
 
        all_string_sub(path, "/", "\\", 0);
 
  decoding:
-       decode_urlpart(path, sizeof(pstring));
-       decode_urlpart(server, sizeof(fstring));
-       decode_urlpart(share, sizeof(fstring));
-       decode_urlpart(user, sizeof(fstring));
-       decode_urlpart(password, sizeof(fstring));
+       decode_urlpart(path, path_len);
+       decode_urlpart(server, server_len);
+       decode_urlpart(share, share_len);
+       decode_urlpart(user, user_len);
+       decode_urlpart(password, password_len);
 
        return 0;
 }
 
 /*
- * Convert an SMB error into a UNIX error ...
+ * Verify that the options specified in a URL are valid
  */
+static int smbc_check_options(char *server, char *share, char *path, char *options)
+{
+        DEBUG(4, ("smbc_check_options(): server='%s' share='%s' path='%s' options='%s'\n", server, share, path, options));
+
+        /* No options at all is always ok */
+        if (! *options) return 0;
+
+        /*
+         * For right now, we only support a very few options possibilities.
+         * No options are supported if server, share, or path are not empty.
+         * If all are empty, then we support the following two choices right
+         * now:
+         *
+         *   mb=.any
+         *   mb=.all
+         */
+        if ((*server || *share || *path) && *options) {
+                /* Invalid: options provided with server, share, or path */
+                DEBUG(1, ("Found unsupported options (%s) with non-empty server, share, or path\n", options));
+                return -1;
+        }
+
+        if (strcmp(options, "mb=.any") != 0 &&
+            strcmp(options, "mb=.all") != 0) {
+                DEBUG(1, ("Found unsupported options (%s)\n", options));
+                return -1;
+        }
 
+        return 0;
+}
+
+/*
+ * Convert an SMB error into a UNIX error ...
+ */
 static int smbc_errno(SMBCCTX *context, struct cli_state *c)
 {
        int ret = cli_errno(c);
@@ -469,6 +562,7 @@ SMBCSRV *smbc_server(SMBCCTX *context,
 
        DEBUG(4,("smbc_server: server_n=[%s] server=[%s]\n", server_n, server));
   
+#if 0 /* djl: obsolete code?  neither group nor p is used beyond here */
        if ((p=strchr_m(server_n,'#')) && 
            (strcmp(p+1,"1D")==0 || strcmp(p+1,"01")==0)) {
     
@@ -477,6 +571,7 @@ SMBCSRV *smbc_server(SMBCCTX *context,
                *p = 0;
                
        }
+#endif
 
        DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));
 
@@ -503,7 +598,6 @@ SMBCSRV *smbc_server(SMBCCTX *context,
                  */
                 c.port = 445;
                 if (!cli_connect(&c, server_n, &ip)) {
-                        cli_shutdown(&c);
                         errno = ENETUNREACH;
                         return NULL;
                 }
@@ -598,6 +692,7 @@ SMBCSRV *smbc_server(SMBCCTX *context,
        DEBUG(2, ("Server connect ok: //%s/%s: %p\n", 
                  server, share, srv));
 
+       DLIST_ADD(context->internal->_servers, srv);
        return srv;
 
  failed:
@@ -648,17 +743,16 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
                                                 password, 0,
                                                 Undefined, NULL);
                 if (! NT_STATUS_IS_OK(nt_status)) {
-                        DEBUG(0,("cli_full_connection failed! (%s)\n",
+                        DEBUG(1,("cli_full_connection failed! (%s)\n",
                                  nt_errstr(nt_status)));
                         errno = ENOTSUP;
                         return NULL;
                 }
 
                 if (!cli_nt_session_open(ipc_cli, PI_LSARPC)) {
-                        DEBUG(0, ("cli_nt_session_open fail! (%s)\n",
-                                  nt_errstr(nt_status)));
+                        DEBUG(1, ("cli_nt_session_open fail!\n"));
                         errno = ENOTSUP;
-                        free(ipc_cli);
+                        cli_shutdown(ipc_cli);
                         return NULL;
                 }
 
@@ -673,14 +767,14 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
         
                 if (!NT_STATUS_IS_OK(nt_status)) {
                         errno = smbc_errno(context, ipc_cli);
-                        free(ipc_cli);
+                        cli_shutdown(ipc_cli);
                         return NULL;
                 }
 
                 ipc_srv = (SMBCSRV *)malloc(sizeof(*ipc_srv));
                 if (!ipc_srv) {
                         errno = ENOMEM;
-                        free(ipc_cli);
+                        cli_shutdown(ipc_cli);
                         return NULL;
                 }
 
@@ -690,14 +784,23 @@ SMBCSRV *smbc_attr_server(SMBCCTX *context,
                 free(ipc_cli);
 
                 /* now add it to the cache (internal or external) */
+
+                errno = 0;      /* let cache function set errno if it likes */
                 if (context->callbacks.add_cached_srv_fn(context, ipc_srv,
                                                          server,
                                                          "IPC$$",
                                                          workgroup,
                                                          username)) {
                         DEBUG(3, (" Failed to add server to cache\n"));
+                        if (errno == 0) {
+                                errno = ENOMEM;
+                        }
+                        cli_shutdown(&ipc_srv->cli);
+                        free(ipc_srv);
                         return NULL;
                 }
+
+                DLIST_ADD(context->internal->_servers, ipc_srv);
         }
 
         return ipc_srv;
@@ -730,7 +833,16 @@ static SMBCFILE *smbc_open_ctx(SMBCCTX *context, const char *fname, int flags, m
 
        }
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return NULL;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -1042,7 +1154,16 @@ static int smbc_unlink_ctx(SMBCCTX *context, const char *fname)
 
        }
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /* FIXME, check errors */
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -1141,11 +1262,23 @@ static int smbc_rename_ctx(SMBCCTX *ocontext, const char *oname,
        
        DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
 
-       smbc_parse_path(ocontext, oname, server1, share1, path1, user1, password1);
+       smbc_parse_path(ocontext, oname,
+                        server1, sizeof(server1),
+                        share1, sizeof(share1),
+                        path1, sizeof(path1),
+                        user1, sizeof(user1),
+                        password1, sizeof(password1),
+                        NULL, 0);
 
        if (user1[0] == (char)0) fstrcpy(user1, ocontext->user);
 
-       smbc_parse_path(ncontext, nname, server2, share2, path2, user2, password2);
+       smbc_parse_path(ncontext, nname,
+                        server2, sizeof(server2),
+                        share2, sizeof(share2),
+                        path2, sizeof(path2),
+                        user2, sizeof(user2),
+                        password2, sizeof(password2),
+                        NULL, 0);
 
        if (user2[0] == (char)0) fstrcpy(user2, ncontext->user);
 
@@ -1348,7 +1481,16 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
   
        DEBUG(4, ("smbc_stat(%s)\n", fname));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -1360,27 +1502,6 @@ static int smbc_stat_ctx(SMBCCTX *context, const char *fname, struct stat *st)
                return -1;  /* errno set by smbc_server */
        }
 
-       /* if (strncmp(srv->cli.dev, "IPC", 3) == 0) {
-
-          mode = aDIR | aRONLY;
-
-          }
-          else if (strncmp(srv->cli.dev, "LPT", 3) == 0) {
-          
-          if (strcmp(path, "\\") == 0) {
-          
-          mode = aDIR | aRONLY;
-
-          }
-          else {
-
-          mode = aRONLY;
-          smbc_stat_printjob(srv, path, &size, &m_time);
-          c_time = a_time = m_time;
-
-          }
-          else { */
-
        if (!smbc_getatr(context, srv, path, &mode, &size, 
                         &c_time, &a_time, &m_time, &ino)) {
 
@@ -1462,16 +1583,7 @@ static int smbc_fstat_ctx(SMBCCTX *context, SMBCFILE *file, struct stat *st)
 
 /*
  * Routine to open a directory
- *
- * We want to allow:
- *
- * smb: which should list all the workgroups available
- * smb:workgroup
- * smb:workgroup//server
- * smb://server
- * smb://server/share
- * smb://<IP-addr> which should list shares on server
- * smb://<IP-addr>/share which should list files on share
+ * We accept the URL syntax explained in smbc_parse_path(), above.
  */
 
 static void smbc_remove_dir(SMBCFILE *dir)
@@ -1536,7 +1648,6 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
                ZERO_STRUCTP(dir->dir_list);
 
                dir->dir_end = dir->dir_next = dir->dir_list;
-  
        }
        else {
 
@@ -1552,7 +1663,6 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
                ZERO_STRUCTP(dir->dir_end->next);
 
                dir->dir_end = dir->dir_end->next;
-
        }
 
        dir->dir_end->next = NULL;
@@ -1575,6 +1685,46 @@ static int add_dirent(SMBCFILE *dir, const char *name, const char *comment, uint
 
 }
 
+static void
+list_unique_wg_fn(const char *name, uint32 type, const char *comment, void *state)
+{
+       SMBCFILE *dir = (SMBCFILE *)state;
+        struct smbc_dir_list *dir_list;
+        struct smbc_dirent *dirent;
+       int dirent_type;
+        int remove = 0;
+
+       dirent_type = dir->dir_type;
+
+       if (add_dirent(dir, name, comment, dirent_type) < 0) {
+
+               /* An error occurred, what do we do? */
+               /* FIXME: Add some code here */
+       }
+
+        /* Point to the one just added */
+        dirent = dir->dir_end->dirent;
+
+        /* See if this was a duplicate */
+        for (dir_list = dir->dir_list;
+             dir_list != dir->dir_end;
+             dir_list = dir_list->next) {
+                if (! remove &&
+                    strcmp(dir_list->dirent->name, dirent->name) == 0) {
+                        /* Duplicate.  End end of list need to be removed. */
+                        remove = 1;
+                }
+
+                if (remove && dir_list->next == dir->dir_end) {
+                        /* Found the end of the list.  Remove it. */
+                        dir->dir_end = dir_list;
+                        free(dir_list->next);
+                        dir_list->next = NULL;
+                        break;
+                }
+        }
+}
+
 static void
 list_fn(const char *name, uint32 type, const char *comment, void *state)
 {
@@ -1615,7 +1765,6 @@ list_fn(const char *name, uint32 type, const char *comment, void *state)
                /* FIXME: Add some code here */
 
        }
-
 }
 
 static void
@@ -1635,7 +1784,7 @@ dir_list_fn(file_info *finfo, const char *mask, void *state)
 
 static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
 {
-       fstring server, share, user, password;
+       fstring server, share, user, password, options;
        pstring workgroup;
        pstring path;
        SMBCSRV *srv  = NULL;
@@ -1656,13 +1805,26 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                return NULL;
        }
 
-       if (smbc_parse_path(context, fname, server, share, path, user, password)) {
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(path),
+                            password, sizeof(password),
+                            options, sizeof(options))) {
                DEBUG(4, ("no valid path\n"));
                errno = EINVAL;
                return NULL;
        }
 
-       DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s'\n", fname, server, share, path));
+       DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' path='%s' options='%s'\n", fname, server, share, path, options));
+
+        /* Ensure the options are valid */
+        if (smbc_check_options(server, share, path, options)) {
+                DEBUG(4, ("unacceptable options (%s)\n", options));
+                errno = EINVAL;
+                return NULL;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -1686,8 +1848,9 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
        dir->file     = False;
        dir->dir_list = dir->dir_next = dir->dir_end = NULL;
 
-       if (server[0] == (char)0) {
-           struct in_addr server_ip;
+       if (server[0] == (char)0 &&
+            (! *options || strcmp(options, "mb=.any") == 0)) {
+                struct in_addr server_ip;
                if (share[0] != (char)0 || path[0] != (char)0) {
 
                        errno = EINVAL;
@@ -1698,9 +1861,11 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                        return NULL;
                }
 
-               /* We have server and share and path empty ... so list the workgroups */
-                /* first try to get the LMB for our workgroup, and if that fails,     */
-                /* try the DMB                                                        */
+               /*
+                 * We have server and share and path empty ... so list the
+                 * workgroups first try to get the LMB for our workgroup, and
+                 * if that fails, try the DMB
+                 */
 
                pstrcpy(workgroup, lp_workgroup());
 
@@ -1726,7 +1891,21 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
 
                    cli_shutdown(cli);
                } else {
-                   if (!name_status_find("*", 0, 0, server_ip, server)) {
+                    /*
+                     * Do a name status query to find out the name of the
+                     * master browser.  We use <01><02>__MSBROWSE__<02>#01 if
+                     * *#00 fails because a domain master browser will not
+                     * respond to a wildcard query (or, at least, an NT4
+                     * server acting as the domain master browser will not).
+                     *
+                     * We might be able to use ONLY the query on MSBROWSE, but
+                     * that's not yet been tested with all Windows versions,
+                     * so until it is, leave the original wildcard query as
+                     * the first choice and fall back to MSBROWSE if the
+                     * wildcard query fails.
+                     */
+                   if (!name_status_find("*", 0, 0x1d, server_ip, server) &&
+                        !name_status_find(MSBROWSE, 1, 0x1d, server_ip, server)) {
                        errno = ENOENT;
                        return NULL;
                    }
@@ -1734,9 +1913,10 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
 
                DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
 
-               /*
-                * Get a connection to IPC$ on the server if we do not already have one
-                */
+                /*
+                 * Get a connection to IPC$ on the server if we do not already
+                 * have one
+                 */
                 
                srv = smbc_server(context, server, "IPC$", workgroup, user, password);
                 if (!srv) {
@@ -1756,6 +1936,7 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_fn,
                                       (void *)dir)) {
 
+                        DEBUG(1, ("Could not enumerate domains using '%s'\n", workgroup));
                        if (dir) {
                                SAFE_FREE(dir->fname);
                                SAFE_FREE(dir);
@@ -1765,9 +1946,99 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                        return NULL;
 
                }
-       }
-       else { /* Server not an empty string ... Check the rest and see what gives */
+        } else if (server[0] == (char)0 &&
+                   (! *options || strcmp(options, "mb=.all") == 0)) {
+
+                int i;
+                int count;
+                struct ip_service *ip_list;
+                struct ip_service server_addr;
+                struct user_auth_info u_info;
+                struct cli_state *cli;
+
+               if (share[0] != (char)0 || path[0] != (char)0) {
+
+                       errno = EINVAL;
+                       if (dir) {
+                               SAFE_FREE(dir->fname);
+                               SAFE_FREE(dir);
+                       }
+                       return NULL;
+               }
+
+                pstrcpy(u_info.username, user);
+                pstrcpy(u_info.password, password);
+
+               /*
+                 * We have server and share and path empty but options
+                 * requesting that we scan all master browsers for their list
+                 * of workgroups/domains.  This implies that we must first try
+                 * broadcast queries to find all master browsers, and if that
+                 * doesn't work, then try our other methods which return only
+                 * a single master browser.
+                 */
+
+                if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) {
+                        if (!find_master_ip(workgroup, &server_addr.ip)) {
+
+                                errno = ENOENT;
+                                return NULL;
+                        }
 
+                        ip_list = &server_addr;
+                        count = 1;
+                }
+
+                for (i = 0; i < count; i++) {
+                        DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip)));
+                        
+                        cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, &u_info);
+                        fstrcpy(server, cli->desthost);
+                        cli_shutdown(cli);
+
+                        DEBUG(4, ("using workgroup %s %s\n", workgroup, server));
+
+                        /*
+                         * For each returned master browser IP address, get a
+                         * connection to IPC$ on the server if we do not
+                         * already have one, and determine the
+                         * workgroups/domains that it knows about.
+                         */
+                
+                        srv = smbc_server(context, server,
+                                          "IPC$", workgroup, user, password);
+                        if (!srv) {
+                                
+                                if (dir) {
+                                        SAFE_FREE(dir->fname);
+                                        SAFE_FREE(dir);
+                                }
+                                return NULL;
+                        }
+                
+                        dir->srv = srv;
+                        dir->dir_type = SMBC_WORKGROUP;
+
+                        /* Now, list the stuff ... */
+                        
+                        if (!cli_NetServerEnum(&srv->cli, workgroup, SV_TYPE_DOMAIN_ENUM, list_unique_wg_fn,
+                                               (void *)dir)) {
+                                
+                                if (dir) {
+                                        SAFE_FREE(dir->fname);
+                                        SAFE_FREE(dir);
+                                }
+                                errno = cli_errno(&srv->cli);
+                                
+                                return NULL;
+                                
+                        }
+                }
+        } else { 
+                /*
+                 * Server not an empty string ... Check the rest and see what
+                 * gives
+                 */
                if (share[0] == (char)0) {
 
                        if (path[0] != (char)0) { /* Should not have empty share with path */
@@ -1796,7 +2067,7 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                                 */
 
 
-                               if (!name_status_find("*", 0, 0, rem_ip, buserver)) {
+                               if (!name_status_find(server, 0, 0, rem_ip, buserver)) {
 
                                        DEBUG(0, ("Could not get name of local/domain master browser for server %s\n", server));
                                        errno = EPERM;  /* FIXME, is this correct */
@@ -1835,7 +2106,6 @@ static SMBCFILE *smbc_opendir_ctx(SMBCCTX *context, const char *fname)
                                        return NULL;
                                        
                                }
-
                        }
                        else {
 
@@ -1962,7 +2232,6 @@ static int smbc_closedir_ctx(SMBCCTX *context, SMBCFILE *dir)
 
                SAFE_FREE(dir->fname);
                SAFE_FREE(dir);    /* Free the space too */
-
        }
 
        return 0;
@@ -1983,6 +2252,7 @@ struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
            !context->internal->_initialized) {
 
                errno = EINVAL;
+                DEBUG(0, ("Invalid context in smbc_readdir_ctx()\n"));
                return NULL;
 
        }
@@ -1990,6 +2260,7 @@ struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
        if (!dir || !DLIST_CONTAINS(context->internal->_files, dir)) {
 
                errno = EBADF;
+                DEBUG(0, ("Invalid dir in smbc_readdir_ctx()\n"));
                return NULL;
 
        }
@@ -1997,16 +2268,17 @@ struct smbc_dirent *smbc_readdir_ctx(SMBCCTX *context, SMBCFILE *dir)
        if (dir->file != False) { /* FIXME, should be dir, perhaps */
 
                errno = ENOTDIR;
+                DEBUG(0, ("Found file vs directory in smbc_readdir_ctx()\n"));
                return NULL;
 
        }
 
-       if (!dir->dir_next)
+       if (!dir->dir_next) {
                return NULL;
+        }
        else {
 
                dirent = dir->dir_next->dirent;
-
                if (!dirent) {
 
                        errno = ENOENT;
@@ -2142,7 +2414,16 @@ static int smbc_mkdir_ctx(SMBCCTX *context, const char *fname, mode_t mode)
   
        DEBUG(4, ("smbc_mkdir(%s)\n", fname));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -2229,7 +2510,17 @@ static int smbc_rmdir_ctx(SMBCCTX *context, const char *fname)
   
        DEBUG(4, ("smbc_rmdir(%s)\n", fname));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0))
+        {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -2466,7 +2757,16 @@ int smbc_chmod_ctx(SMBCCTX *context, const char *fname, mode_t newmode)
   
        DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, newmode));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -2518,7 +2818,16 @@ int smbc_utimes_ctx(SMBCCTX *context, const char *fname, struct timeval *tbuf)
   
        DEBUG(4, ("smbc_utimes(%s, [%s])\n", fname, ctime(&t)));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -3353,7 +3662,16 @@ int smbc_setxattr_ctx(SMBCCTX *context,
        DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
                   fname, name, (int) size, (char *) value));
 
-       smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+       if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
        if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -3485,7 +3803,16 @@ int smbc_getxattr_ctx(SMBCCTX *context,
   
         DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
 
-        smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+        if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -3568,7 +3895,16 @@ int smbc_removexattr_ctx(SMBCCTX *context,
   
         DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
 
-        smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+        if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -3700,7 +4036,16 @@ static SMBCFILE *smbc_open_print_job_ctx(SMBCCTX *context, const char *fname)
   
         DEBUG(4, ("smbc_open_print_job_ctx(%s)\n", fname));
 
-        smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+        if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return NULL;
+        }
 
         /* What if the path is empty, or the file exists? */
 
@@ -3814,7 +4159,16 @@ static int smbc_list_print_jobs_ctx(SMBCCTX *context, const char *fname, smbc_li
   
         DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
 
-        smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+        if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
         
@@ -3867,7 +4221,16 @@ static int smbc_unlink_print_job_ctx(SMBCCTX *context, const char *fname, int id
   
         DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
 
-        smbc_parse_path(context, fname, server, share, path, user, password); /*FIXME, errors*/
+        if (smbc_parse_path(context, fname,
+                            server, sizeof(server),
+                            share, sizeof(share),
+                            path, sizeof(path),
+                            user, sizeof(user),
+                            password, sizeof(password),
+                            NULL, 0)) {
+                errno = EINVAL;
+                return -1;
+        }
 
         if (user[0] == (char)0) fstrcpy(user, context->user);
 
@@ -3989,13 +4352,17 @@ int smbc_free_context(SMBCCTX * context, int shutdown_ctx)
                 /* First try to remove the servers the nice way. */
                 if (context->callbacks.purge_cached_fn(context)) {
                         SMBCSRV * s;
+                        SMBCSRV * next;
                         DEBUG(1, ("Could not purge all servers, Nice way shutdown failed.\n"));
                         s = context->internal->_servers;
                         while (s) {
+                                DEBUG(1, ("Forced shutdown: %p (fd=%d)\n", s, s->cli.fd));
                                 cli_shutdown(&s->cli);
                                 context->callbacks.remove_cached_srv_fn(context, s);
+                                next = s->next;
+                                DLIST_REMOVE(context->internal->_servers, s);
                                 SAFE_FREE(s);
-                                s = s->next;
+                                s = next;
                         }
                         context->internal->_servers = NULL;
                 }
index 72546947e4dce5f0a52c9084441f4d2fd0553db6..82a4b21754e63e0ccffb76b9cd9e7137aff816a0 100644 (file)
@@ -1443,7 +1443,7 @@ BOOL cli_nt_session_open(struct cli_state *cli, const int pipe_idx)
                cli->nt_pipe_fnum = (uint16)fnum;
        } else {
                if ((fnum = cli_open(cli, pipe_names[pipe_idx].client_pipe, O_CREAT|O_RDWR, DENY_NONE)) == -1) {
-                       DEBUG(0,("cli_nt_session_open: cli_open failed on pipe %s to machine %s.  Error was %s\n",
+                       DEBUG(1,("cli_nt_session_open: cli_open failed on pipe %s to machine %s.  Error was %s\n",
                                 pipe_names[pipe_idx].client_pipe, cli->desthost, cli_errstr(cli)));
                        return False;
                }