/* * Remove a server from the cached server list it's unused. * On success, 0 is returned. 1 is returned if the server could not be removed. * * Also useable outside libsmbclient */ int SMBC_remove_unused_server(SMBCCTX * context, SMBCSRV * srv) { SMBCFILE * file; /* are we being fooled ? */ if (!context || !context->internal->initialized || !srv) { return 1; } /* Check all open files/directories for a relation with this server */ for (file = context->internal->files; file; file = file->next) { if (file->srv == srv) { /* Still used */ DEBUG(3, ("smbc_remove_usused_server: " "%p still used by %p.\n", srv, file)); return 1; } } DLIST_REMOVE(context->internal->servers, srv); cli_shutdown(srv->cli); srv->cli = NULL; DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv)); smbc_getFunctionRemoveCachedServer(context)(context, srv); SAFE_FREE(srv); return 0; }
static SMBCSRV * SMBC_server_internal(TALLOC_CTX *ctx, SMBCCTX *context, bool connect_if_not_found, const char *server, uint16_t port, const char *share, char **pp_workgroup, char **pp_username, char **pp_password, bool *in_cache) { SMBCSRV *srv=NULL; char *workgroup = NULL; struct cli_state *c = NULL; const char *server_n = server; int is_ipc = (share != NULL && strcmp(share, "IPC$") == 0); uint32_t fs_attrs = 0; const char *username_used; NTSTATUS status; char *newserver, *newshare; int flags = 0; struct smbXcli_tcon *tcon = NULL; int signing_state = SMB_SIGNING_DEFAULT; ZERO_STRUCT(c); *in_cache = false; if (server[0] == 0) { errno = EPERM; return NULL; } /* Look for a cached connection */ srv = SMBC_find_server(ctx, context, server, share, pp_workgroup, pp_username, pp_password); /* * If we found a connection and we're only allowed one share per * server... */ if (srv && share != NULL && *share != '\0' && smbc_getOptionOneSharePerServer(context)) { /* * ... then if there's no current connection to the share, * connect to it. SMBC_find_server(), or rather the function * pointed to by context->get_cached_srv_fn which * was called by SMBC_find_server(), will have issued a tree * disconnect if the requested share is not the same as the * one that was already connected. */ /* * Use srv->cli->desthost and srv->cli->share instead of * server and share below to connect to the actual share, * i.e., a normal share or a referred share from * 'msdfs proxy' share. */ if (!cli_state_has_tcon(srv->cli)) { /* Ensure we have accurate auth info */ SMBC_call_auth_fn(ctx, context, smbXcli_conn_remote_name(srv->cli->conn), srv->cli->share, pp_workgroup, pp_username, pp_password); if (!*pp_workgroup || !*pp_username || !*pp_password) { errno = ENOMEM; cli_shutdown(srv->cli); srv->cli = NULL; smbc_getFunctionRemoveCachedServer(context)(context, srv); return NULL; } /* * We don't need to renegotiate encryption * here as the encryption context is not per * tid. */ status = cli_tree_connect(srv->cli, srv->cli->share, "?????", *pp_password, strlen(*pp_password)+1); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); cli_shutdown(srv->cli); srv->cli = NULL; smbc_getFunctionRemoveCachedServer(context)(context, srv); srv = NULL; } /* Determine if this share supports case sensitivity */ if (is_ipc) { DEBUG(4, ("IPC$ so ignore case sensitivity\n")); status = NT_STATUS_OK; } else { status = cli_get_fs_attr_info(c, &fs_attrs); } if (!NT_STATUS_IS_OK(status)) { DEBUG(4, ("Could not retrieve " "case sensitivity flag: %s.\n", nt_errstr(status))); /* * We can't determine the case sensitivity of * the share. We have no choice but to use the * user-specified case sensitivity setting. */ if (smbc_getOptionCaseSensitive(context)) { cli_set_case_sensitive(c, True); } else { cli_set_case_sensitive(c, False); } } else if (!is_ipc) { DEBUG(4, ("Case sensitive: %s\n", (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? "True" : "False"))); cli_set_case_sensitive( c, (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? True : False)); } /* * Regenerate the dev value since it's based on both * server and share */ if (srv) { const char *remote_name = smbXcli_conn_remote_name(srv->cli->conn); srv->dev = (dev_t)(str_checksum(remote_name) ^ str_checksum(srv->cli->share)); } } } /* If we have a connection... */ if (srv) { /* ... then we're done here. Give 'em what they came for. */ *in_cache = true; goto done; } /* If we're not asked to connect when a connection doesn't exist... */ if (! connect_if_not_found) { /* ... then we're done here. */ return NULL; } if (!*pp_workgroup || !*pp_username || !*pp_password) { errno = ENOMEM; return NULL; } DEBUG(4,("SMBC_server: server_n=[%s] server=[%s]\n", server_n, server)); DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); status = NT_STATUS_UNSUCCESSFUL; if (smbc_getOptionUseKerberos(context)) { flags |= CLI_FULL_CONNECTION_USE_KERBEROS; } if (smbc_getOptionFallbackAfterKerberos(context)) { flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS; } if (smbc_getOptionUseCCache(context)) { flags |= CLI_FULL_CONNECTION_USE_CCACHE; } if (smbc_getOptionUseNTHash(context)) { flags |= CLI_FULL_CONNECTION_USE_NT_HASH; } if (context->internal->smb_encryption_level != SMBC_ENCRYPTLEVEL_NONE) { signing_state = SMB_SIGNING_REQUIRED; } if (port == 0) { if (share == NULL || *share == '\0' || is_ipc) { /* * Try 139 first for IPC$ */ status = cli_connect_nb(server_n, NULL, NBT_SMB_PORT, 0x20, smbc_getNetbiosName(context), signing_state, flags, &c); } } if (!NT_STATUS_IS_OK(status)) { /* * No IPC$ or 139 did not work */ status = cli_connect_nb(server_n, NULL, port, 0x20, smbc_getNetbiosName(context), signing_state, flags, &c); } if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); return NULL; } cli_set_timeout(c, smbc_getTimeout(context)); status = smbXcli_negprot(c->conn, c->timeout, lp_client_min_protocol(), lp_client_max_protocol()); if (!NT_STATUS_IS_OK(status)) { cli_shutdown(c); errno = ETIMEDOUT; return NULL; } if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { /* Ensure we ask for some initial credits. */ smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS); } username_used = *pp_username; if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, *pp_password, strlen(*pp_password), *pp_password, strlen(*pp_password), *pp_workgroup))) { /* Failed. Try an anonymous login, if allowed by flags. */ username_used = ""; if (smbc_getOptionNoAutoAnonymousLogin(context) || !NT_STATUS_IS_OK(cli_session_setup(c, username_used, *pp_password, 1, *pp_password, 0, *pp_workgroup))) { cli_shutdown(c); errno = EPERM; return NULL; } } DEBUG(4,(" session setup ok\n")); /* here's the fun part....to support 'msdfs proxy' shares (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL here before trying to connect to the original share. cli_check_msdfs_proxy() will fail if it is a normal share. */ if (smbXcli_conn_dfs_supported(c->conn) && cli_check_msdfs_proxy(ctx, c, share, &newserver, &newshare, /* FIXME: cli_check_msdfs_proxy() does not support smbc_smb_encrypt_level type */ context->internal->smb_encryption_level ? true : false, *pp_username, *pp_password, *pp_workgroup)) { cli_shutdown(c); srv = SMBC_server_internal(ctx, context, connect_if_not_found, newserver, port, newshare, pp_workgroup, pp_username, pp_password, in_cache); TALLOC_FREE(newserver); TALLOC_FREE(newshare); return srv; } /* must be a normal share */ status = cli_tree_connect(c, share, "?????", *pp_password, strlen(*pp_password)+1); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); cli_shutdown(c); return NULL; } DEBUG(4,(" tconx ok\n")); if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { tcon = c->smb2.tcon; } else { tcon = c->smb1.tcon; } /* Determine if this share supports case sensitivity */ if (is_ipc) { DEBUG(4, ("IPC$ so ignore case sensitivity\n")); status = NT_STATUS_OK; } else { status = cli_get_fs_attr_info(c, &fs_attrs); } if (!NT_STATUS_IS_OK(status)) { DEBUG(4, ("Could not retrieve case sensitivity flag: %s.\n", nt_errstr(status))); /* * We can't determine the case sensitivity of the share. We * have no choice but to use the user-specified case * sensitivity setting. */ if (smbc_getOptionCaseSensitive(context)) { cli_set_case_sensitive(c, True); } else { cli_set_case_sensitive(c, False); } } else if (!is_ipc) { DEBUG(4, ("Case sensitive: %s\n", (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? "True" : "False"))); smbXcli_tcon_set_fs_attributes(tcon, fs_attrs); } if (context->internal->smb_encryption_level) { /* Attempt UNIX smb encryption. */ if (!NT_STATUS_IS_OK(cli_force_encryption(c, username_used, *pp_password, *pp_workgroup))) { /* * context->smb_encryption_level == 1 * means don't fail if encryption can't be negotiated, * == 2 means fail if encryption can't be negotiated. */ DEBUG(4,(" SMB encrypt failed\n")); if (context->internal->smb_encryption_level == 2) { cli_shutdown(c); errno = EPERM; return NULL; } } DEBUG(4,(" SMB encrypt ok\n")); } /* * Ok, we have got a nice connection * Let's allocate a server structure. */ srv = SMB_MALLOC_P(SMBCSRV); if (!srv) { cli_shutdown(c); errno = ENOMEM; return NULL; } ZERO_STRUCTP(srv); DLIST_ADD(srv->cli, c); srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); srv->no_pathinfo = False; srv->no_pathinfo2 = False; srv->no_pathinfo3 = False; srv->no_nt_session = False; done: if (!pp_workgroup || !*pp_workgroup || !**pp_workgroup) { workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context)); } else { workgroup = *pp_workgroup; } if(!workgroup) { if (c != NULL) { cli_shutdown(c); } SAFE_FREE(srv); return NULL; } /* set the credentials to make DFS work */ smbc_set_credentials_with_fallback(context, workgroup, *pp_username, *pp_password); return srv; }
SMBCSRV * SMBC_find_server(TALLOC_CTX *ctx, SMBCCTX *context, const char *server, const char *share, char **pp_workgroup, char **pp_username, char **pp_password) { SMBCSRV *srv; int auth_called = 0; if (!pp_workgroup || !pp_username || !pp_password) { return NULL; } check_server_cache: srv = smbc_getFunctionGetCachedServer(context)(context, server, share, *pp_workgroup, *pp_username); if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] || !*pp_password || !(*pp_password)[0])) { SMBC_call_auth_fn(ctx, context, server, share, pp_workgroup, pp_username, pp_password); /* * However, smbc_auth_fn may have picked up info relating to * an existing connection, so try for an existing connection * again ... */ auth_called = 1; goto check_server_cache; } if (srv) { if (smbc_getFunctionCheckServer(context)(context, srv)) { /* * This server is no good anymore * Try to remove it and check for more possible * servers in the cache */ if (smbc_getFunctionRemoveUnusedServer(context)(context, srv)) { /* * We could not remove the server completely, * remove it from the cache so we will not get * it again. It will be removed when the last * file/dir is closed. */ smbc_getFunctionRemoveCachedServer(context)(context, srv); } /* * Maybe there are more cached connections to this * server */ goto check_server_cache; } return srv; } return NULL; }
static SMBCSRV * SMBC_server_internal(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, bool *in_cache) { SMBCSRV *srv=NULL; char *workgroup = NULL; struct cli_state *c; struct nmb_name called, calling; const char *server_n = server; struct sockaddr_storage ss; int tried_reverse = 0; int port_try_first; int port_try_next; int is_ipc = (share != NULL && strcmp(share, "IPC$") == 0); uint32 fs_attrs = 0; const char *username_used; NTSTATUS status; char *newserver, *newshare; zero_sockaddr(&ss); ZERO_STRUCT(c); *in_cache = false; if (server[0] == 0) { errno = EPERM; return NULL; } /* Look for a cached connection */ srv = SMBC_find_server(ctx, context, server, share, pp_workgroup, pp_username, pp_password); /* * If we found a connection and we're only allowed one share per * server... */ if (srv && *share != '\0' && smbc_getOptionOneSharePerServer(context)) { /* * ... then if there's no current connection to the share, * connect to it. SMBC_find_server(), or rather the function * pointed to by context->get_cached_srv_fn which * was called by SMBC_find_server(), will have issued a tree * disconnect if the requested share is not the same as the * one that was already connected. */ /* * Use srv->cli->desthost and srv->cli->share instead of * server and share below to connect to the actual share, * i.e., a normal share or a referred share from * 'msdfs proxy' share. */ if (srv->cli->cnum == (uint16) -1) { /* Ensure we have accurate auth info */ SMBC_call_auth_fn(ctx, context, srv->cli->desthost, srv->cli->share, pp_workgroup, pp_username, pp_password); if (!*pp_workgroup || !*pp_username || !*pp_password) { errno = ENOMEM; cli_shutdown(srv->cli); srv->cli = NULL; smbc_getFunctionRemoveCachedServer(context)(context, srv); return NULL; } /* * We don't need to renegotiate encryption * here as the encryption context is not per * tid. */ status = cli_tcon_andx(srv->cli, srv->cli->share, "?????", *pp_password, strlen(*pp_password)+1); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); cli_shutdown(srv->cli); srv->cli = NULL; smbc_getFunctionRemoveCachedServer(context)(context, srv); srv = NULL; } /* Determine if this share supports case sensitivity */ if (is_ipc) { DEBUG(4, ("IPC$ so ignore case sensitivity\n")); } else if (!cli_get_fs_attr_info(c, &fs_attrs)) { DEBUG(4, ("Could not retrieve " "case sensitivity flag: %s.\n", cli_errstr(c))); /* * We can't determine the case sensitivity of * the share. We have no choice but to use the * user-specified case sensitivity setting. */ if (smbc_getOptionCaseSensitive(context)) { cli_set_case_sensitive(c, True); } else { cli_set_case_sensitive(c, False); } } else { DEBUG(4, ("Case sensitive: %s\n", (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? "True" : "False"))); cli_set_case_sensitive( c, (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? True : False)); } /* * Regenerate the dev value since it's based on both * server and share */ if (srv) { srv->dev = (dev_t)(str_checksum(srv->cli->desthost) ^ str_checksum(srv->cli->share)); } } } /* If we have a connection... */ if (srv) { /* ... then we're done here. Give 'em what they came for. */ *in_cache = true; goto done; } /* If we're not asked to connect when a connection doesn't exist... */ if (! connect_if_not_found) { /* ... then we're done here. */ return NULL; } if (!*pp_workgroup || !*pp_username || !*pp_password) { errno = ENOMEM; return NULL; } make_nmb_name(&calling, smbc_getNetbiosName(context), 0x0); make_nmb_name(&called , server, 0x20); DEBUG(4,("SMBC_server: server_n=[%s] server=[%s]\n", server_n, server)); DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server)); again: zero_sockaddr(&ss); /* have to open a new connection */ if ((c = cli_initialise()) == NULL) { errno = ENOMEM; return NULL; } if (smbc_getOptionUseKerberos(context)) { c->use_kerberos = True; } if (smbc_getOptionFallbackAfterKerberos(context)) { c->fallback_after_kerberos = True; } if (smbc_getOptionUseCCache(context)) { c->use_ccache = True; } c->timeout = smbc_getTimeout(context); /* * Force use of port 139 for first try if share is $IPC, empty, or * null, so browse lists can work */ if (share == NULL || *share == '\0' || is_ipc) { port_try_first = 139; port_try_next = 445; } else { port_try_first = 445; port_try_next = 139; } c->port = port_try_first; status = cli_connect(c, server_n, &ss); if (!NT_STATUS_IS_OK(status)) { /* First connection attempt failed. Try alternate port. */ c->port = port_try_next; status = cli_connect(c, server_n, &ss); if (!NT_STATUS_IS_OK(status)) { cli_shutdown(c); errno = ETIMEDOUT; return NULL; } } if (!cli_session_request(c, &calling, &called)) { cli_shutdown(c); if (strcmp(called.name, "*SMBSERVER")) { make_nmb_name(&called , "*SMBSERVER", 0x20); goto again; } else { /* Try one more time, but ensure we don't loop */ /* Only try this if server is an IP address ... */ if (is_ipaddress(server) && !tried_reverse) { fstring remote_name; struct sockaddr_storage rem_ss; if (!interpret_string_addr(&rem_ss, server, NI_NUMERICHOST)) { DEBUG(4, ("Could not convert IP address " "%s to struct sockaddr_storage\n", server)); errno = ETIMEDOUT; return NULL; } tried_reverse++; /* Yuck */ if (name_status_find("*", 0, 0, &rem_ss, remote_name)) { make_nmb_name(&called, remote_name, 0x20); goto again; } } } errno = ETIMEDOUT; return NULL; } DEBUG(4,(" session request ok\n")); status = cli_negprot(c); if (!NT_STATUS_IS_OK(status)) { cli_shutdown(c); errno = ETIMEDOUT; return NULL; } username_used = *pp_username; if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used, *pp_password, strlen(*pp_password), *pp_password, strlen(*pp_password), *pp_workgroup))) { fprintf(stderr, "JerryLin: Libsmb_server.c->SMBC_server_internal: cli_session_setup fail with username=[%s], password=[%s]\n", username_used, *pp_password); /* Failed. Try an anonymous login, if allowed by flags. */ username_used = ""; if (smbc_getOptionNoAutoAnonymousLogin(context) || !NT_STATUS_IS_OK(cli_session_setup(c, username_used, *pp_password, 1, *pp_password, 0, *pp_workgroup))) { cli_shutdown(c); errno = EPERM; return NULL; } } status = cli_init_creds(c, username_used, *pp_workgroup, *pp_password); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); cli_shutdown(c); return NULL; } DEBUG(4,(" session setup ok\n")); /* here's the fun part....to support 'msdfs proxy' shares (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL here before trying to connect to the original share. cli_check_msdfs_proxy() will fail if it is a normal share. */ if ((c->capabilities & CAP_DFS) && cli_check_msdfs_proxy(ctx, c, share, &newserver, &newshare, /* FIXME: cli_check_msdfs_proxy() does not support smbc_smb_encrypt_level type */ context->internal->smb_encryption_level ? true : false, *pp_username, *pp_password, *pp_workgroup)) { cli_shutdown(c); srv = SMBC_server_internal(ctx, context, connect_if_not_found, newserver, newshare, pp_workgroup, pp_username, pp_password, in_cache); TALLOC_FREE(newserver); TALLOC_FREE(newshare); return srv; } /* must be a normal share */ status = cli_tcon_andx(c, share, "?????", *pp_password, strlen(*pp_password)+1); if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); fprintf(stderr, "JerryLin: Libsmb_server.c->SMBC_server_internal: cli_tcon_andx return %08X, errno=[%d]\n", NT_STATUS_V(status), errno); cli_shutdown(c); return NULL; } DEBUG(4,(" tconx ok\n")); /* Determine if this share supports case sensitivity */ if (is_ipc) { DEBUG(4, ("IPC$ so ignore case sensitivity\n")); } else if (!cli_get_fs_attr_info(c, &fs_attrs)) { DEBUG(4, ("Could not retrieve case sensitivity flag: %s.\n", cli_errstr(c))); /* * We can't determine the case sensitivity of the share. We * have no choice but to use the user-specified case * sensitivity setting. */ if (smbc_getOptionCaseSensitive(context)) { cli_set_case_sensitive(c, True); } else { cli_set_case_sensitive(c, False); } } else { DEBUG(4, ("Case sensitive: %s\n", (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? "True" : "False"))); cli_set_case_sensitive(c, (fs_attrs & FILE_CASE_SENSITIVE_SEARCH ? True : False)); } if (context->internal->smb_encryption_level) { /* Attempt UNIX smb encryption. */ if (!NT_STATUS_IS_OK(cli_force_encryption(c, username_used, *pp_password, *pp_workgroup))) { /* * context->smb_encryption_level == 1 * means don't fail if encryption can't be negotiated, * == 2 means fail if encryption can't be negotiated. */ DEBUG(4,(" SMB encrypt failed\n")); if (context->internal->smb_encryption_level == 2) { cli_shutdown(c); errno = EPERM; return NULL; } } DEBUG(4,(" SMB encrypt ok\n")); } /* * Ok, we have got a nice connection * Let's allocate a server structure. */ srv = SMB_MALLOC_P(SMBCSRV); if (!srv) { cli_shutdown(c); errno = ENOMEM; return NULL; } ZERO_STRUCTP(srv); srv->cli = c; srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); srv->no_pathinfo = False; srv->no_pathinfo2 = False; srv->no_nt_session = False; done: if (!pp_workgroup || !*pp_workgroup || !**pp_workgroup) { workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context)); } else { workgroup = *pp_workgroup; } if(!workgroup) { return NULL; } /* set the credentials to make DFS work */ smbc_set_credentials_with_fallback(context, workgroup, *pp_username, *pp_password); return srv; }
/* * Free a context * * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed * and thus you'll be leaking memory if not handled properly. * */ int smbc_free_context(SMBCCTX *context, int shutdown_ctx) { if (!context) { errno = EBADF; return 1; } if (shutdown_ctx) { SMBCFILE * f; DEBUG(1,("Performing aggressive shutdown.\n")); f = context->internal->files; while (f) { smbc_getFunctionClose(context)(context, f); f = f->next; } context->internal->files = NULL; /* First try to remove the servers the nice way. */ if (smbc_getFunctionPurgeCachedServers(context)(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); smbc_getFunctionRemoveCachedServer(context)(context, s); next = s->next; DLIST_REMOVE(context->internal->servers, s); SAFE_FREE(s); s = next; } context->internal->servers = NULL; } } else { /* This is the polite way */ if (smbc_getFunctionPurgeCachedServers(context)(context)) { DEBUG(1, ("Could not purge all servers, " "free_context failed.\n")); errno = EBUSY; return 1; } if (context->internal->servers) { DEBUG(1, ("Active servers in context, " "free_context failed.\n")); errno = EBUSY; return 1; } if (context->internal->files) { DEBUG(1, ("Active files in context, " "free_context failed.\n")); errno = EBUSY; return 1; } } /* Things we have to clean up */ free(smbc_getWorkgroup(context)); smbc_setWorkgroup(context, NULL); free(smbc_getNetbiosName(context)); smbc_setNetbiosName(context, NULL); free(smbc_getUser(context)); smbc_setUser(context, NULL); DEBUG(3, ("Context %p successfully freed\n", context)); /* Free any DFS auth context. */ TALLOC_FREE(context->internal->auth_info); SAFE_FREE(context->internal); SAFE_FREE(context); /* Protect access to the count of contexts in use */ if (SMB_THREAD_LOCK(initialized_ctx_count_mutex) != 0) { smb_panic("error locking 'initialized_ctx_count'"); } if (initialized_ctx_count) { initialized_ctx_count--; } if (initialized_ctx_count == 0) { SMBC_module_terminate(); } /* Unlock the mutex */ if (SMB_THREAD_UNLOCK(initialized_ctx_count_mutex) != 0) { smb_panic("error unlocking 'initialized_ctx_count'"); } return 0; }
/* * Free a context * * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed * and thus you'll be leaking memory if not handled properly. * */ int smbc_free_context(SMBCCTX *context, int shutdown_ctx) { if (!context) { errno = EBADF; return 1; } if (shutdown_ctx) { SMBCFILE * f; DEBUG(1,("Performing aggressive shutdown.\n")); f = context->internal->files; while (f) { smbc_getFunctionClose(context)(context, f); f = f->next; } context->internal->files = NULL; /* First try to remove the servers the nice way. */ if (smbc_getFunctionPurgeCachedServers(context)(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); smbc_getFunctionRemoveCachedServer(context)(context, s); next = s->next; DLIST_REMOVE(context->internal->servers, s); SAFE_FREE(s); s = next; } context->internal->servers = NULL; } } else { /* This is the polite way */ if (smbc_getFunctionPurgeCachedServers(context)(context)) { DEBUG(1, ("Could not purge all servers, " "free_context failed.\n")); errno = EBUSY; return 1; } if (context->internal->servers) { DEBUG(1, ("Active servers in context, " "free_context failed.\n")); errno = EBUSY; return 1; } if (context->internal->files) { DEBUG(1, ("Active files in context, " "free_context failed.\n")); errno = EBUSY; return 1; } } /* Things we have to clean up */ free(smbc_getWorkgroup(context)); smbc_setWorkgroup(context, NULL); free(smbc_getNetbiosName(context)); smbc_setNetbiosName(context, NULL); free(smbc_getUser(context)); smbc_setUser(context, NULL); DEBUG(3, ("Context %p successfully freed\n", context)); SAFE_FREE(context->internal); SAFE_FREE(context); if (initialized_ctx_count) { initialized_ctx_count--; } if (initialized_ctx_count == 0 && SMBC_initialized) { gencache_shutdown(); secrets_shutdown(); gfree_all(); SMBC_initialized = false; } return 0; }