diff options
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/cliconnect.c | 86 | ||||
-rw-r--r-- | source3/libsmb/clientgen.c | 11 | ||||
-rw-r--r-- | source3/libsmb/libsmb_cache.c | 9 | ||||
-rw-r--r-- | source3/libsmb/libsmbclient.c | 557 |
4 files changed, 546 insertions, 117 deletions
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 84159e5d62..e75a361e25 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -1590,42 +1590,88 @@ 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_bcast(pstring workgroup, struct user_auth_info *user_info) +struct cli_state *get_ipc_connect_master_ip(struct ip_service * mb_ip, pstring workgroup, struct user_auth_info *user_info) { - struct ip_service *ip_list; + static fstring name; struct cli_state *cli; - int i, count; struct in_addr server_ip; - /* Go looking for workgroups by broadcasting on the local network */ + 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)) { - if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) { - return False; + DEBUG(99, ("Could not retrieve name status for %s\n", + inet_ntoa(mb_ip->ip))); + return NULL; } - for (i = 0; i < count; i++) { - static fstring name; - - if (!name_status_find("*", 0, 0x1d, ip_list[i].ip, name)) - continue; - - if (!find_master_ip(name, &server_ip)) - continue; + 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(ip_list[i].ip))); + name, inet_ntoa(mb_ip->ip))); cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info); - if (!cli) - continue; - 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) +{ + struct ip_service *ip_list; + struct cli_state *cli; + int i, count; + + 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++) { + DEBUG(99, ("Found master browser %s\n", inet_ntoa(ip_list[i].ip))); + + cli = get_ipc_connect_master_ip(&ip_list[i], workgroup, user_info); + if (cli) + return(cli); } return NULL; diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index 0873700fc0..8542eea064 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -367,6 +367,17 @@ 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. + */ + if ( cli->cnum != (uint16)-1 ) + cli_tdis(cli); + cli_nt_session_close(cli); cli_nt_netlogon_netsec_session_close(cli); diff --git a/source3/libsmb/libsmb_cache.c b/source3/libsmb/libsmb_cache.c index 67dc686b48..cb40b4aaa6 100644 --- a/source3/libsmb/libsmb_cache.c +++ b/source3/libsmb/libsmb_cache.c @@ -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; diff --git a/source3/libsmb/libsmbclient.c b/source3/libsmb/libsmbclient.c index 35e8a9786b..68bb6661eb 100644 --- a/source3/libsmb/libsmbclient.c +++ b/source3/libsmb/libsmbclient.c @@ -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; @@ -1576,6 +1686,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) { SMBCFILE *dir = (SMBCFILE *)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; } |