summaryrefslogtreecommitdiffstats
path: root/source3/libsmb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/libsmb
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/ABI/smbclient-0.1.0.sigs172
-rw-r--r--source3/libsmb/ABI/smbclient-0.2.0.sigs172
-rw-r--r--source3/libsmb/ABI/smbclient-0.2.1.sigs174
-rw-r--r--source3/libsmb/ABI/smbclient-0.2.2.sigs176
-rw-r--r--source3/libsmb/ABI/smbclient-0.2.3.sigs179
-rw-r--r--source3/libsmb/ABI/smbclient-0.3.0.sigs179
-rw-r--r--source3/libsmb/ABI/smbclient-0.3.1.sigs180
-rw-r--r--source3/libsmb/ABI/smbclient-0.3.2.sigs181
-rw-r--r--source3/libsmb/ABI/smbclient-0.3.3.sigs184
-rw-r--r--source3/libsmb/ABI/smbclient-0.4.0.sigs184
-rw-r--r--source3/libsmb/ABI/smbclient-0.5.0.sigs185
-rw-r--r--source3/libsmb/ABI/smbclient-0.6.0.sigs188
-rw-r--r--source3/libsmb/ABI/smbclient-0.7.0.sigs188
-rw-r--r--source3/libsmb/ABI/smbclient-0.8.0.sigs190
-rw-r--r--source3/libsmb/async_smb.c269
-rw-r--r--source3/libsmb/auth_generic.c229
-rw-r--r--source3/libsmb/cli_smb2_fnum.c5040
-rw-r--r--source3/libsmb/cli_smb2_fnum.h324
-rw-r--r--source3/libsmb/cliconnect.c4026
-rw-r--r--source3/libsmb/clidfs.c1451
-rw-r--r--source3/libsmb/clidgram.c482
-rw-r--r--source3/libsmb/clidgram.h54
-rw-r--r--source3/libsmb/clientgen.c650
-rw-r--r--source3/libsmb/clierror.c119
-rw-r--r--source3/libsmb/clifile.c7486
-rw-r--r--source3/libsmb/clifsinfo.c815
-rw-r--r--source3/libsmb/clilist.c1255
-rw-r--r--source3/libsmb/climessage.c411
-rw-r--r--source3/libsmb/clioplock.c169
-rw-r--r--source3/libsmb/cliprint.c219
-rw-r--r--source3/libsmb/cliquota.c696
-rw-r--r--source3/libsmb/clirap.c1853
-rw-r--r--source3/libsmb/clirap.h206
-rw-r--r--source3/libsmb/clireadwrite.c1907
-rw-r--r--source3/libsmb/clisecdesc.c372
-rw-r--r--source3/libsmb/clisymlink.c650
-rw-r--r--source3/libsmb/clitrans.c224
-rw-r--r--source3/libsmb/conncache.c227
-rw-r--r--source3/libsmb/dsgetdcname.c1274
-rw-r--r--source3/libsmb/dsgetdcname.h43
-rw-r--r--source3/libsmb/errormap.c295
-rw-r--r--source3/libsmb/errormap_wbc.c62
-rw-r--r--source3/libsmb/errormap_wbc.h29
-rw-r--r--source3/libsmb/libsmb.h31
-rw-r--r--source3/libsmb/libsmb_cache.c242
-rw-r--r--source3/libsmb/libsmb_compat.c581
-rw-r--r--source3/libsmb/libsmb_context.c807
-rw-r--r--source3/libsmb/libsmb_dir.c2726
-rw-r--r--source3/libsmb/libsmb_file.c803
-rw-r--r--source3/libsmb/libsmb_misc.c46
-rw-r--r--source3/libsmb/libsmb_path.c416
-rw-r--r--source3/libsmb/libsmb_printjob.c335
-rw-r--r--source3/libsmb/libsmb_server.c910
-rw-r--r--source3/libsmb/libsmb_setget.c1155
-rw-r--r--source3/libsmb/libsmb_stat.c538
-rw-r--r--source3/libsmb/libsmb_thread_impl.c127
-rw-r--r--source3/libsmb/libsmb_thread_posix.c52
-rw-r--r--source3/libsmb/libsmb_xattr.c2406
-rw-r--r--source3/libsmb/namecache.c480
-rw-r--r--source3/libsmb/namequery.c3538
-rw-r--r--source3/libsmb/namequery.h116
-rw-r--r--source3/libsmb/namequery_dc.c262
-rw-r--r--source3/libsmb/nmblib.c1466
-rw-r--r--source3/libsmb/nmblib.h59
-rw-r--r--source3/libsmb/passchange.c323
-rw-r--r--source3/libsmb/proto.h1080
-rw-r--r--source3/libsmb/pylibsmb.c3030
-rw-r--r--source3/libsmb/samlogon_cache.c405
-rw-r--r--source3/libsmb/samlogon_cache.h46
-rw-r--r--source3/libsmb/smbclient.pc.in11
-rw-r--r--source3/libsmb/smberr.c224
-rw-r--r--source3/libsmb/smbsock_connect.c870
-rw-r--r--source3/libsmb/trusts_util.c630
-rw-r--r--source3/libsmb/unexpected.c751
-rw-r--r--source3/libsmb/unexpected.h49
-rw-r--r--source3/libsmb/wscript30
76 files changed, 57914 insertions, 0 deletions
diff --git a/source3/libsmb/ABI/smbclient-0.1.0.sigs b/source3/libsmb/ABI/smbclient-0.1.0.sigs
new file mode 100644
index 0000000..3dea59c
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.1.0.sigs
@@ -0,0 +1,172 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setUser: void (SMBCCTX *, char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.2.0.sigs b/source3/libsmb/ABI/smbclient-0.2.0.sigs
new file mode 100644
index 0000000..aa85859
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.2.0.sigs
@@ -0,0 +1,172 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.2.1.sigs b/source3/libsmb/ABI/smbclient-0.2.1.sigs
new file mode 100644
index 0000000..1b7f6ce
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.2.1.sigs
@@ -0,0 +1,174 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.2.2.sigs b/source3/libsmb/ABI/smbclient-0.2.2.sigs
new file mode 100644
index 0000000..fdafdd0
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.2.2.sigs
@@ -0,0 +1,176 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.2.3.sigs b/source3/libsmb/ABI/smbclient-0.2.3.sigs
new file mode 100644
index 0000000..cda537c
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.2.3.sigs
@@ -0,0 +1,179 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.3.0.sigs b/source3/libsmb/ABI/smbclient-0.3.0.sigs
new file mode 100644
index 0000000..736f32a
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.3.0.sigs
@@ -0,0 +1,179 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.3.1.sigs b/source3/libsmb/ABI/smbclient-0.3.1.sigs
new file mode 100644
index 0000000..d41274d
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.3.1.sigs
@@ -0,0 +1,180 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.3.2.sigs b/source3/libsmb/ABI/smbclient-0.3.2.sigs
new file mode 100644
index 0000000..2f089d8
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.3.2.sigs
@@ -0,0 +1,181 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.3.3.sigs b/source3/libsmb/ABI/smbclient-0.3.3.sigs
new file mode 100644
index 0000000..833d0df
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.3.3.sigs
@@ -0,0 +1,184 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: char *(SMBCCTX *)
+smbc_getWorkgroup: char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.4.0.sigs b/source3/libsmb/ABI/smbclient-0.4.0.sigs
new file mode 100644
index 0000000..170cb52
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.4.0.sigs
@@ -0,0 +1,184 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: const char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: const char *(SMBCCTX *)
+smbc_getWorkgroup: const char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, const char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, const char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.5.0.sigs b/source3/libsmb/ABI/smbclient-0.5.0.sigs
new file mode 100644
index 0000000..b424597
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.5.0.sigs
@@ -0,0 +1,185 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: const char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: const char *(SMBCCTX *)
+smbc_getWorkgroup: const char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, const char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, const char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.6.0.sigs b/source3/libsmb/ABI/smbclient-0.6.0.sigs
new file mode 100644
index 0000000..ee758e2
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.6.0.sigs
@@ -0,0 +1,188 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus2: smbc_readdirplus2_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: const char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: const char *(SMBCCTX *)
+smbc_getWorkgroup: const char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_readdirplus2: const struct libsmb_file_info *(unsigned int, struct stat *)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionReaddirPlus2: void (SMBCCTX *, smbc_readdirplus2_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, const char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, const char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.7.0.sigs b/source3/libsmb/ABI/smbclient-0.7.0.sigs
new file mode 100644
index 0000000..ee758e2
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.7.0.sigs
@@ -0,0 +1,188 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus2: smbc_readdirplus2_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: const char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: const char *(SMBCCTX *)
+smbc_getWorkgroup: const char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_readdirplus2: const struct libsmb_file_info *(unsigned int, struct stat *)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionReaddirPlus2: void (SMBCCTX *, smbc_readdirplus2_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, const char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, const char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/ABI/smbclient-0.8.0.sigs b/source3/libsmb/ABI/smbclient-0.8.0.sigs
new file mode 100644
index 0000000..fc7e79f
--- /dev/null
+++ b/source3/libsmb/ABI/smbclient-0.8.0.sigs
@@ -0,0 +1,190 @@
+smbc_chmod: int (const char *, mode_t)
+smbc_close: int (int)
+smbc_closedir: int (int)
+smbc_creat: int (const char *, mode_t)
+smbc_fgetxattr: int (int, const char *, const void *, size_t)
+smbc_flistxattr: int (int, char *, size_t)
+smbc_free_context: int (SMBCCTX *, int)
+smbc_fremovexattr: int (int, const char *)
+smbc_fsetxattr: int (int, const char *, const void *, size_t, int)
+smbc_fstat: int (int, struct stat *)
+smbc_fstatvfs: int (int, struct statvfs *)
+smbc_ftruncate: int (int, off_t)
+smbc_getDebug: int (SMBCCTX *)
+smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *)
+smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *)
+smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *)
+smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *)
+smbc_getFunctionClose: smbc_close_fn (SMBCCTX *)
+smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *)
+smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *)
+smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *)
+smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *)
+smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *)
+smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *)
+smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *)
+smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *)
+smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *)
+smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *)
+smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *)
+smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *)
+smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *)
+smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *)
+smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *)
+smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *)
+smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *)
+smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *)
+smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *)
+smbc_getFunctionRead: smbc_read_fn (SMBCCTX *)
+smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *)
+smbc_getFunctionReaddirPlus2: smbc_readdirplus2_fn (SMBCCTX *)
+smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *)
+smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *)
+smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *)
+smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *)
+smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *)
+smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *)
+smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *)
+smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *)
+smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *)
+smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *)
+smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *)
+smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *)
+smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *)
+smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *)
+smbc_getNetbiosName: const char *(SMBCCTX *)
+smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *)
+smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *)
+smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *)
+smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *)
+smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *)
+smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *)
+smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *)
+smbc_getOptionPosixExtensions: smbc_bool (SMBCCTX *)
+smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *)
+smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *)
+smbc_getOptionUseCCache: smbc_bool (SMBCCTX *)
+smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *)
+smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *)
+smbc_getOptionUserData: void *(SMBCCTX *)
+smbc_getPort: uint16_t (SMBCCTX *)
+smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *)
+smbc_getTimeout: int (SMBCCTX *)
+smbc_getUser: const char *(SMBCCTX *)
+smbc_getWorkgroup: const char *(SMBCCTX *)
+smbc_getdents: int (unsigned int, struct smbc_dirent *, int)
+smbc_getxattr: int (const char *, const char *, const void *, size_t)
+smbc_init: int (smbc_get_auth_data_fn, int)
+smbc_init_context: SMBCCTX *(SMBCCTX *)
+smbc_lgetxattr: int (const char *, const char *, const void *, size_t)
+smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn)
+smbc_listxattr: int (const char *, char *, size_t)
+smbc_llistxattr: int (const char *, char *, size_t)
+smbc_lremovexattr: int (const char *, const char *)
+smbc_lseek: off_t (int, off_t, int)
+smbc_lseekdir: int (int, off_t)
+smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_mkdir: int (const char *, mode_t)
+smbc_new_context: SMBCCTX *(void)
+smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *)
+smbc_open: int (const char *, int, mode_t)
+smbc_open_print_job: int (const char *)
+smbc_opendir: int (const char *)
+smbc_option_get: void *(SMBCCTX *, char *)
+smbc_option_set: void (SMBCCTX *, char *, ...)
+smbc_print_file: int (const char *, const char *)
+smbc_read: ssize_t (int, void *, size_t)
+smbc_readdir: struct smbc_dirent *(unsigned int)
+smbc_readdirplus: const struct libsmb_file_info *(unsigned int)
+smbc_readdirplus2: const struct libsmb_file_info *(unsigned int, struct stat *)
+smbc_removexattr: int (const char *, const char *)
+smbc_rename: int (const char *, const char *)
+smbc_rmdir: int (const char *)
+smbc_setConfiguration: int (SMBCCTX *, const char *)
+smbc_setDebug: void (SMBCCTX *, int)
+smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn)
+smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn)
+smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn)
+smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn)
+smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn)
+smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn)
+smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn)
+smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn)
+smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn)
+smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn)
+smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn)
+smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn)
+smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn)
+smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn)
+smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn)
+smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn)
+smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn)
+smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn)
+smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn)
+smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn)
+smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn)
+smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn)
+smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn)
+smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn)
+smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn)
+smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn)
+smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn)
+smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn)
+smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn)
+smbc_setFunctionReaddirPlus2: void (SMBCCTX *, smbc_readdirplus2_fn)
+smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn)
+smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn)
+smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn)
+smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn)
+smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn)
+smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn)
+smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn)
+smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn)
+smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn)
+smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn)
+smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn)
+smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn)
+smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn)
+smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn)
+smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn)
+smbc_setNetbiosName: void (SMBCCTX *, const char *)
+smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int)
+smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool)
+smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool)
+smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool)
+smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode)
+smbc_setOptionPosixExtensions: void (SMBCCTX *, smbc_bool)
+smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *)
+smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level)
+smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool)
+smbc_setOptionUserData: void (SMBCCTX *, void *)
+smbc_setPort: void (SMBCCTX *, uint16_t)
+smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *)
+smbc_setTimeout: void (SMBCCTX *, int)
+smbc_setUser: void (SMBCCTX *, const char *)
+smbc_setWorkgroup: void (SMBCCTX *, const char *)
+smbc_set_context: SMBCCTX *(SMBCCTX *)
+smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *)
+smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *)
+smbc_setxattr: int (const char *, const char *, const void *, size_t, int)
+smbc_stat: int (const char *, struct stat *)
+smbc_statvfs: int (char *, struct statvfs *)
+smbc_telldir: off_t (int)
+smbc_unlink: int (const char *)
+smbc_unlink_print_job: int (const char *, int)
+smbc_urldecode: int (char *, char *, size_t)
+smbc_urlencode: int (char *, char *, int)
+smbc_utime: int (const char *, struct utimbuf *)
+smbc_utimes: int (const char *, struct timeval *)
+smbc_version: const char *(void)
+smbc_write: ssize_t (int, const void *, size_t)
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c
new file mode 100644
index 0000000..38e0bb9
--- /dev/null
+++ b/source3/libsmb/async_smb.c
@@ -0,0 +1,269 @@
+/*
+ Unix SMB/CIFS implementation.
+ Infrastructure for async SMB client requests
+ Copyright (C) Volker Lendecke 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+struct cli_smb_req_state {
+ struct cli_state *cli;
+ uint8_t smb_command;
+ struct tevent_req *req;
+ struct cli_smb_req_state **ptr;
+};
+
+static int cli_smb_req_state_destructor(struct cli_smb_req_state *state)
+{
+ talloc_set_destructor(state->ptr, NULL);
+ talloc_free(state->ptr);
+ return 0;
+}
+
+static int cli_smb_req_state_ptr_destructor(struct cli_smb_req_state **ptr)
+{
+ struct cli_smb_req_state *state = *ptr;
+ void *parent = talloc_parent(state);
+
+ talloc_set_destructor(state, NULL);
+
+ talloc_reparent(state, parent, state->req);
+ talloc_free(state);
+ return 0;
+}
+
+struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint8_t smb_command,
+ uint8_t additional_flags,
+ uint16_t additional_flags2,
+ uint8_t wct, uint16_t *vwv,
+ int iov_count,
+ struct iovec *bytes_iov)
+{
+ struct cli_smb_req_state *state;
+ uint8_t clear_flags = 0;
+ uint16_t clear_flags2 = 0;
+
+ state = talloc_zero(mem_ctx, struct cli_smb_req_state);
+ if (state == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->smb_command = smb_command;
+ state->ptr = talloc(state, struct cli_smb_req_state *);
+ if (state->ptr == NULL) {
+ talloc_free(state);
+ return NULL;
+ }
+ *state->ptr = state;
+
+ state->req = smb1cli_req_create(state, ev, cli->conn, smb_command,
+ additional_flags, clear_flags,
+ additional_flags2, clear_flags2,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ wct, vwv, iov_count, bytes_iov);
+ if (state->req == NULL) {
+ talloc_free(state);
+ return NULL;
+ }
+
+ talloc_reparent(state, state->req, state->ptr);
+ talloc_set_destructor(state, cli_smb_req_state_destructor);
+ talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
+
+ return state->req;
+}
+
+struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint8_t smb_command,
+ uint8_t additional_flags,
+ uint16_t additional_flags2,
+ uint8_t wct, uint16_t *vwv,
+ uint32_t num_bytes,
+ const uint8_t *bytes)
+{
+ struct cli_smb_req_state *state;
+ uint8_t clear_flags = 0;
+ uint16_t clear_flags2 = 0;
+
+ state = talloc_zero(mem_ctx, struct cli_smb_req_state);
+ if (state == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->smb_command = smb_command;
+ state->ptr = talloc(state, struct cli_smb_req_state *);
+ if (state->ptr == NULL) {
+ talloc_free(state);
+ return NULL;
+ }
+ *state->ptr = state;
+
+ state->req = smb1cli_req_send(state, ev, cli->conn, smb_command,
+ additional_flags, clear_flags,
+ additional_flags2, clear_flags2,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ wct, vwv, num_bytes, bytes);
+ if (state->req == NULL) {
+ talloc_free(state);
+ return NULL;
+ }
+
+ talloc_reparent(state, state->req, state->ptr);
+ talloc_set_destructor(state, cli_smb_req_state_destructor);
+ talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor);
+
+ return state->req;
+}
+
+NTSTATUS cli_smb_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
+ uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
+ uint32_t *pnum_bytes, uint8_t **pbytes)
+{
+ NTSTATUS status;
+ void *parent = talloc_parent(req);
+ struct cli_smb_req_state *state =
+ talloc_get_type(parent,
+ struct cli_smb_req_state);
+ struct iovec *recv_iov = NULL;
+ uint8_t wct = 0;
+ uint16_t *vwv = NULL;
+ uint32_t num_bytes;
+ uint8_t *bytes = NULL;
+ uint8_t *inbuf;
+ bool is_expected = false;
+ bool map_dos_errors = true;
+
+ if (pinbuf != NULL) {
+ *pinbuf = NULL;
+ }
+ if (pwct != NULL) {
+ *pwct = 0;
+ }
+ if (pvwv != NULL) {
+ *pvwv = NULL;
+ }
+ if (pnum_bytes != NULL) {
+ *pnum_bytes = 0;
+ }
+ if (pbytes != NULL) {
+ *pbytes = NULL;
+ }
+
+ status = smb1cli_req_recv(req, req,
+ &recv_iov,
+ NULL, /* phdr */
+ &wct,
+ &vwv,
+ NULL, /* pvwv_offset */
+ &num_bytes,
+ &bytes,
+ NULL, /* pbytes_offset */
+ &inbuf,
+ NULL, 0); /* expected */
+
+ if (state) {
+ if ((state->smb_command == SMBsesssetupX) &&
+ NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /*
+ * NT_STATUS_MORE_PROCESSING_REQUIRED is a
+ * valid return code for session setup
+ */
+ is_expected = true;
+ }
+
+ if ((state->smb_command == SMBntcreateX) &&
+ NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ min_wct = 0;
+ }
+
+ map_dos_errors = state->cli->map_dos_errors;
+ state->cli->raw_status = status;
+ talloc_free(state->ptr);
+ state = NULL;
+ }
+
+ if (NT_STATUS_IS_DOS(status) && map_dos_errors) {
+ uint8_t eclass = NT_STATUS_DOS_CLASS(status);
+ uint16_t ecode = NT_STATUS_DOS_CODE(status);
+ /*
+ * TODO: is it really a good idea to do a mapping here?
+ *
+ * The old cli_pull_error() also does it, so I do not change
+ * the behavior yet.
+ */
+ status = dos_to_ntstatus(eclass, ecode);
+ }
+
+ if (!NT_STATUS_IS_ERR(status)) {
+ is_expected = true;
+ }
+
+ if (!is_expected) {
+ TALLOC_FREE(recv_iov);
+ return status;
+ }
+
+ if (wct < min_wct) {
+ TALLOC_FREE(recv_iov);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (pwct != NULL) {
+ *pwct = wct;
+ }
+ if (pvwv != NULL) {
+ *pvwv = vwv;
+ }
+ if (pnum_bytes != NULL) {
+ *pnum_bytes = num_bytes;
+ }
+ if (pbytes != NULL) {
+ *pbytes = bytes;
+ }
+
+ if (pinbuf != NULL && mem_ctx != NULL) {
+ if (talloc_reference_count(inbuf) == 0) {
+ *pinbuf = talloc_move(mem_ctx, &inbuf);
+ TALLOC_FREE(recv_iov);
+ } else {
+ *pinbuf = inbuf;
+ }
+ } else if (mem_ctx != NULL) {
+ if (talloc_reference_count(inbuf) == 0) {
+ (void)talloc_move(mem_ctx, &inbuf);
+ TALLOC_FREE(recv_iov);
+ }
+ }
+
+ return status;
+}
diff --git a/source3/libsmb/auth_generic.c b/source3/libsmb/auth_generic.c
new file mode 100644
index 0000000..e5120a0
--- /dev/null
+++ b/source3/libsmb/auth_generic.c
@@ -0,0 +1,229 @@
+/*
+ NLTMSSP wrappers
+
+ Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Andrew Bartlett 2001-2003,2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth_generic.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc.h"
+#include "lib/param/param.h"
+#include "librpc/crypto/gse.h"
+
+NTSTATUS auth_generic_set_username(struct auth_generic_state *ans,
+ const char *user)
+{
+ cli_credentials_set_username(ans->credentials, user, CRED_SPECIFIED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_set_domain(struct auth_generic_state *ans,
+ const char *domain)
+{
+ cli_credentials_set_domain(ans->credentials, domain, CRED_SPECIFIED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_set_password(struct auth_generic_state *ans,
+ const char *password)
+{
+ cli_credentials_set_password(ans->credentials, password, CRED_SPECIFIED);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_set_creds(struct auth_generic_state *ans,
+ struct cli_credentials *creds)
+{
+ talloc_unlink(ans->credentials, creds);
+ ans->credentials = creds;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_client_prepare(TALLOC_CTX *mem_ctx, struct auth_generic_state **auth_generic_state)
+{
+ struct auth_generic_state *ans;
+ NTSTATUS nt_status;
+ size_t idx = 0;
+ struct gensec_settings *gensec_settings;
+ const struct gensec_security_ops **backends = NULL;
+ struct loadparm_context *lp_ctx;
+ bool ok;
+
+ ans = talloc_zero(mem_ctx, struct auth_generic_state);
+ if (!ans) {
+ DEBUG(0,("auth_generic_start: talloc failed!\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lp_ctx = loadparm_init_s3(ans, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ DEBUG(10, ("loadparm_init_s3 failed\n"));
+ TALLOC_FREE(ans);
+ return NT_STATUS_INVALID_SERVER_STATE;
+ }
+
+ gensec_settings = lpcfg_gensec_settings(ans, lp_ctx);
+ if (lp_ctx == NULL) {
+ DEBUG(10, ("lpcfg_gensec_settings failed\n"));
+ TALLOC_FREE(ans);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ backends = talloc_zero_array(gensec_settings,
+ const struct gensec_security_ops *, 7);
+ if (backends == NULL) {
+ TALLOC_FREE(ans);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gensec_settings->backends = backends;
+
+ gensec_init();
+
+ /* These need to be in priority order, krb5 before NTLMSSP */
+#if defined(HAVE_KRB5)
+ backends[idx++] = &gensec_gse_krb5_security_ops;
+#endif
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
+ backends[idx++] = gensec_security_by_name(NULL, "ntlmssp_resume_ccache");
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+ backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_SCHANNEL);
+ backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM);
+
+ nt_status = gensec_client_start(ans, &ans->gensec_security, gensec_settings);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(ans);
+ return nt_status;
+ }
+
+ ans->credentials = cli_credentials_init(ans);
+ if (!ans->credentials) {
+ TALLOC_FREE(ans);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = cli_credentials_guess(ans->credentials, lp_ctx);
+ if (!ok) {
+ TALLOC_FREE(ans);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ talloc_unlink(ans, lp_ctx);
+ talloc_unlink(ans, gensec_settings);
+
+ *auth_generic_state = ans;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_client_start(struct auth_generic_state *ans, const char *oid)
+{
+ NTSTATUS status;
+
+ /* Transfer the credentials to gensec */
+ status = gensec_set_credentials(ans->gensec_security, ans->credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ talloc_unlink(ans, ans->credentials);
+ ans->credentials = NULL;
+
+ status = gensec_start_mech_by_oid(ans->gensec_security,
+ oid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_client_start_by_name(struct auth_generic_state *ans,
+ const char *name)
+{
+ NTSTATUS status;
+
+ /* Transfer the credentials to gensec */
+ status = gensec_set_credentials(ans->gensec_security, ans->credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ talloc_unlink(ans, ans->credentials);
+ ans->credentials = NULL;
+
+ status = gensec_start_mech_by_name(ans->gensec_security, name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_client_start_by_authtype(struct auth_generic_state *ans,
+ uint8_t auth_type,
+ uint8_t auth_level)
+{
+ NTSTATUS status;
+
+ /* Transfer the credentials to gensec */
+ status = gensec_set_credentials(ans->gensec_security, ans->credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ talloc_unlink(ans, ans->credentials);
+ ans->credentials = NULL;
+
+ status = gensec_start_mech_by_authtype(ans->gensec_security,
+ auth_type, auth_level);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS auth_generic_client_start_by_sasl(struct auth_generic_state *ans,
+ const char **sasl_list)
+{
+ NTSTATUS status;
+
+ /* Transfer the credentials to gensec */
+ status = gensec_set_credentials(ans->gensec_security, ans->credentials);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC credentials: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ talloc_unlink(ans, ans->credentials);
+ ans->credentials = NULL;
+
+ status = gensec_start_mech_by_sasl_list(ans->gensec_security, sasl_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
new file mode 100644
index 0000000..08d95cf
--- /dev/null
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -0,0 +1,5040 @@
+/*
+ Unix SMB/CIFS implementation.
+ smb2 lib
+ Copyright (C) Jeremy Allison 2013
+ Copyright (C) Volker Lendecke 2013
+ Copyright (C) Stefan Metzmacher 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This code is a thin wrapper around the existing
+ cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
+ but allows the handles to be mapped to uint16_t fnums,
+ which are easier for smbclient to use.
+*/
+
+#include "includes.h"
+#include "client.h"
+#include "async_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "cli_smb2_fnum.h"
+#include "trans2.h"
+#include "clirap.h"
+#include "../libcli/smb/smb2_create_blob.h"
+#include "libsmb/proto.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "lib/util_ea.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "ntioctl.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_smb3posix.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/util/idtree.h"
+
+struct smb2_hnd {
+ uint64_t fid_persistent;
+ uint64_t fid_volatile;
+};
+
+/*
+ * Handle mapping code.
+ */
+
+/***************************************************************
+ Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
+ Ensures handle is owned by cli struct.
+***************************************************************/
+
+static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
+ const struct smb2_hnd *ph, /* In */
+ uint16_t *pfnum) /* Out */
+{
+ int ret;
+ struct idr_context *idp = cli->smb2.open_handles;
+ struct smb2_hnd *owned_h = talloc_memdup(cli,
+ ph,
+ sizeof(struct smb2_hnd));
+
+ if (owned_h == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (idp == NULL) {
+ /* Lazy init */
+ cli->smb2.open_handles = idr_init(cli);
+ if (cli->smb2.open_handles == NULL) {
+ TALLOC_FREE(owned_h);
+ return NT_STATUS_NO_MEMORY;
+ }
+ idp = cli->smb2.open_handles;
+ }
+
+ ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
+ if (ret == -1) {
+ TALLOC_FREE(owned_h);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *pfnum = (uint16_t)ret;
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Return the smb2_hnd pointer associated with the given fnum.
+***************************************************************/
+
+static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
+ uint16_t fnum, /* In */
+ struct smb2_hnd **pph) /* Out */
+{
+ struct idr_context *idp = cli->smb2.open_handles;
+
+ if (idp == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *pph = (struct smb2_hnd *)idr_find(idp, fnum);
+ if (*pph == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Delete the fnum to smb2_hnd mapping. Zeros out handle on
+ successful return.
+***************************************************************/
+
+static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
+ struct smb2_hnd **pph, /* In */
+ uint16_t fnum) /* In */
+{
+ struct idr_context *idp = cli->smb2.open_handles;
+ struct smb2_hnd *ph;
+
+ if (idp == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ph = (struct smb2_hnd *)idr_find(idp, fnum);
+ if (ph != *pph) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ idr_remove(idp, fnum);
+ TALLOC_FREE(*pph);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Oplock mapping code.
+***************************************************************/
+
+static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
+{
+ if (create_flags.batch_oplock) {
+ return SMB2_OPLOCK_LEVEL_BATCH;
+ } else if (create_flags.exclusive_oplock) {
+ return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ }
+
+ /* create_flags doesn't do a level2 request. */
+ return SMB2_OPLOCK_LEVEL_NONE;
+}
+
+/***************************************************************
+ If we're on a DFS share, ensure we convert to a full DFS path
+ if this hasn't already been done.
+***************************************************************/
+
+static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ char *path)
+{
+ bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
+ smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ bool is_already_dfs_path = false;
+
+ if (!is_dfs) {
+ return path;
+ }
+ is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
+ if (is_already_dfs_path) {
+ return path;
+ }
+ if (path[0] == '\0') {
+ return talloc_asprintf(ctx,
+ "%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ }
+ while (*path == '\\') {
+ path++;
+ }
+ return talloc_asprintf(ctx,
+ "%s\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share,
+ path);
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 create to return a uint16_t fnum.
+***************************************************************/
+
+struct cli_smb2_create_fnum_state {
+ struct cli_state *cli;
+ struct smb2_create_blobs in_cblobs;
+ struct smb2_create_blobs out_cblobs;
+ struct smb_create_returns cr;
+ struct symlink_reparse_struct *symlink;
+ uint16_t fnum;
+ struct tevent_req *subreq;
+};
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_create_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_in,
+ struct cli_smb2_create_flags create_flags,
+ uint32_t impersonation_level,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ const struct smb2_create_blobs *in_cblobs)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_create_fnum_state *state;
+ char *fname = NULL;
+ size_t fname_len = 0;
+ bool have_twrp;
+ NTTIME ntt;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_create_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ fname = talloc_strdup(state, fname_in);
+ if (tevent_req_nomem(fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (cli->backup_intent) {
+ create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ if (cli->smb2.client_smb311_posix) {
+ uint8_t modebuf[4] = {
+ 0,
+ };
+
+ status =
+ smb2_create_blob_add(state,
+ &state->in_cblobs,
+ SMB2_CREATE_TAG_POSIX,
+ (DATA_BLOB){
+ .data = modebuf,
+ .length = sizeof(modebuf),
+ });
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
+ have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
+ if (have_twrp) {
+ status = smb2_create_blob_add(
+ state,
+ &state->in_cblobs,
+ SMB2_CREATE_TAG_TWRP,
+ (DATA_BLOB) {
+ .data = (uint8_t *)&ntt,
+ .length = sizeof(ntt),
+ });
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (in_cblobs != NULL) {
+ uint32_t i;
+ for (i=0; i<in_cblobs->num_blobs; i++) {
+ struct smb2_create_blob *b = &in_cblobs->blobs[i];
+ status = smb2_create_blob_add(
+ state, &state->in_cblobs, b->tag, b->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
+ }
+
+ fname = smb2_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ fname_len = strlen(fname);
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname == '\\') {
+ fname++;
+ fname_len--;
+ }
+
+ /* Or end in a '\' */
+ if (fname_len > 0 && fname[fname_len-1] == '\\') {
+ fname[fname_len-1] = '\0';
+ }
+
+ subreq = smb2cli_create_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname,
+ flags_to_smb2_oplock(create_flags),
+ impersonation_level,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ &state->in_cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
+
+ state->subreq = subreq;
+ tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
+
+ return req;
+}
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ struct smb2_hnd h;
+ NTSTATUS status;
+
+ status = smb2cli_create_recv(
+ subreq,
+ &h.fid_persistent,
+ &h.fid_volatile, &state->cr,
+ state,
+ &state->out_cblobs,
+ &state->symlink);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
+{
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ return tevent_req_cancel(state->subreq);
+}
+
+NTSTATUS cli_smb2_create_fnum_recv(
+ struct tevent_req *req,
+ uint16_t *pfnum,
+ struct smb_create_returns *cr,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_create_blobs *out_cblobs,
+ struct symlink_reparse_struct **symlink)
+{
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
+ (symlink != NULL)) {
+ *symlink = talloc_move(mem_ctx, &state->symlink);
+ }
+ state->cli->raw_status = status;
+ return status;
+ }
+ if (pfnum != NULL) {
+ *pfnum = state->fnum;
+ }
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ if (out_cblobs != NULL) {
+ *out_cblobs = (struct smb2_create_blobs) {
+ .num_blobs = state->out_cblobs.num_blobs,
+ .blobs = talloc_move(
+ mem_ctx, &state->out_cblobs.blobs),
+ };
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_create_fnum(
+ struct cli_state *cli,
+ const char *fname,
+ struct cli_smb2_create_flags create_flags,
+ uint32_t impersonation_level,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ const struct smb2_create_blobs *in_cblobs,
+ uint16_t *pfid,
+ struct smb_create_returns *cr,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_create_blobs *out_cblobs)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_create_fnum_send(
+ frame,
+ ev,
+ cli,
+ fname,
+ create_flags,
+ impersonation_level,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ in_cblobs);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_create_fnum_recv(
+ req, pfid, cr, mem_ctx, out_cblobs, NULL);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 close to use a uint16_t fnum.
+***************************************************************/
+
+struct cli_smb2_close_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+};
+
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t flags)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_close_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_close_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_close_send(state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ flags,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
+ return req;
+}
+
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_close_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_close_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_close_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /* Delete the fnum -> handle mapping. */
+ status = delete_smb2_handle_mapping(state->cli, &state->ph,
+ state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
+{
+ struct cli_smb2_close_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_close_fnum_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ }
+ tevent_req_received(req);
+ return status;
+}
+
+NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_close_fnum_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_set_info_fnum_state {
+ uint8_t dummy;
+};
+
+static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_set_info_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_set_info_fnum_state *state = NULL;
+ struct smb2_hnd *ph = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_set_info_send(
+ state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ in_info_type,
+ in_info_class,
+ in_input_buffer,
+ in_additional_info,
+ ph->fid_persistent,
+ ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
+ return req;
+}
+
+static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = smb2cli_set_info_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_smb2_set_info_fnum(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ bool ok;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_set_info_fnum_send(
+ frame,
+ ev,
+ cli,
+ fnum,
+ in_info_type,
+ in_info_class,
+ in_input_buffer,
+ in_additional_info);
+ if (req == NULL) {
+ goto fail;
+ }
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ goto fail;
+ }
+ status = cli_smb2_set_info_fnum_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_delete_on_close_state {
+ struct cli_state *cli;
+ uint8_t data[1];
+ DATA_BLOB inbuf;
+};
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool flag)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb2_delete_on_close_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+ uint8_t in_info_type;
+ uint8_t in_file_info_class;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_delete_on_close_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ /*
+ * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
+ */
+ in_info_type = 1;
+ in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
+ /* Setup data array. */
+ SCVAL(&state->data[0], 0, flag ? 1 : 0);
+ state->inbuf.data = &state->data[0];
+ state->inbuf.length = 1;
+
+ subreq = cli_smb2_set_info_fnum_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ in_info_type,
+ in_file_info_class,
+ &state->inbuf,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_delete_on_close_done,
+ req);
+ return req;
+}
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
+{
+ struct cli_smb2_delete_on_close_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_delete_on_close_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ tevent_req_received(req);
+ return status;
+ }
+
+ state->cli->raw_status = NT_STATUS_OK;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_delete_on_close_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_mkdir_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+};
+
+static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
+static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_mkdir_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_mkdir_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_mkdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ /* Ensure this is a directory. */
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ dname, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE, /* share_access */
+ FILE_CREATE, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
+ return req;
+}
+
+static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_mkdir_state *state = tevent_req_data(
+ req, struct cli_smb2_mkdir_state);
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
+}
+
+static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_smb2_rmdir_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *dname;
+ const struct smb2_create_blobs *in_cblobs;
+ uint16_t fnum;
+ NTSTATUS status;
+};
+
+static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
+static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
+static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
+static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_rmdir_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname,
+ const struct smb2_create_blobs *in_cblobs)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_rmdir_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->dname = dname;
+ state->in_cblobs = in_cblobs;
+
+ subreq = cli_smb2_create_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->dname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ state->in_cblobs); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
+ return req;
+}
+
+static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rmdir_state *state = tevent_req_data(
+ req, struct cli_smb2_rmdir_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ subreq = cli_smb2_create_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->dname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE|
+ FILE_DELETE_ON_CLOSE|
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ state->in_cblobs); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_smb2_delete_on_close_send(
+ state, state->ev, state->cli, state->fnum, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
+}
+
+static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rmdir_state *state = tevent_req_data(
+ req, struct cli_smb2_rmdir_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_smb2_delete_on_close_send(
+ state, state->ev, state->cli, state->fnum, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
+}
+
+static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rmdir_state *state = tevent_req_data(
+ req, struct cli_smb2_rmdir_state);
+
+ state->status = cli_smb2_delete_on_close_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ /*
+ * Close the fd even if the set_disp failed
+ */
+
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
+}
+
+static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
+{
+ struct cli_smb2_rmdir_state *state = tevent_req_data(
+ req, struct cli_smb2_rmdir_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ return state->status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to unlink a pathname.
+***************************************************************/
+
+struct cli_smb2_unlink_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ const struct smb2_create_blobs *in_cblobs;
+};
+
+static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
+static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
+static void cli_smb2_unlink_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_unlink_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ const struct smb2_create_blobs *in_cblobs)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_unlink_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fname = fname;
+ state->in_cblobs = in_cblobs;
+
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx */
+ state->ev, /* tevent_context */
+ state->cli, /* cli_struct */
+ state->fname, /* filename */
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DELETE_ON_CLOSE, /* create_options */
+ state->in_cblobs); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
+ return req;
+}
+
+static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_unlink_state *state = tevent_req_data(
+ req, struct cli_smb2_unlink_state);
+ uint16_t fnum = 0xffff;
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx */
+ state->ev, /* tevent_context */
+ state->cli, /* cli_struct */
+ state->fname, /* filename */
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DELETE_ON_CLOSE|
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ state->in_cblobs); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
+}
+
+static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_unlink_state *state = tevent_req_data(
+ req, struct cli_smb2_unlink_state);
+ uint16_t fnum = 0xffff;
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
+}
+
+static void cli_smb2_unlink_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/***************************************************************
+ Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
+***************************************************************/
+
+static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
+ uint32_t dir_data_length,
+ struct file_info *finfo,
+ uint32_t *next_offset)
+{
+ struct smb3_file_posix_information info = {};
+ size_t consumed;
+ enum ndr_err_code ndr_err;
+ size_t namelen = 0;
+ size_t ret = 0;
+ uint32_t _next_offset = 0;
+
+ if (dir_data_length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ _next_offset = IVAL(dir_data, 0);
+
+ if (_next_offset > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (_next_offset != 0) {
+ /* Ensure we only read what in this record. */
+ dir_data_length = _next_offset;
+ }
+
+ /*
+ * Skip NextEntryOffset and FileIndex
+ */
+ if (dir_data_length < 8) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ dir_data += 8;
+ dir_data_length -= 8;
+
+ ndr_err = ndr_pull_struct_blob_noalloc(
+ dir_data,
+ dir_data_length,
+ &info,
+ (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
+ &consumed);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ if (consumed > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ dir_data += consumed;
+ dir_data_length -= consumed;
+
+ finfo->btime_ts = interpret_long_date(info.creation_time);
+ finfo->atime_ts = interpret_long_date(info.last_access_time);
+ finfo->mtime_ts = interpret_long_date(info.last_write_time);
+ finfo->ctime_ts = interpret_long_date(info.change_time);
+ finfo->allocated_size = info.allocation_size;
+ finfo->size = info.end_of_file;
+ finfo->attr = info.file_attributes;
+ finfo->ino = info.inode;
+ finfo->st_ex_dev = info.device;
+ finfo->st_ex_nlink = info.cc.nlinks;
+ finfo->reparse_tag = info.cc.reparse_tag;
+ finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
+ sid_copy(&finfo->owner_sid, &info.cc.owner);
+ sid_copy(&finfo->group_sid, &info.cc.group);
+
+ if (dir_data_length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ namelen = PULL_LE_U32(dir_data, 0);
+
+ dir_data += 4;
+ dir_data_length -= 4;
+
+ if (namelen > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->name,
+ dir_data,
+ namelen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (finfo->name == NULL) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *next_offset = _next_offset;
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
+***************************************************************/
+
+static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
+ uint32_t dir_data_length,
+ struct file_info *finfo,
+ uint32_t *next_offset)
+{
+ size_t namelen = 0;
+ size_t slen = 0;
+ size_t ret = 0;
+
+ if (dir_data_length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ *next_offset = IVAL(dir_data, 0);
+
+ if (*next_offset > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (*next_offset != 0) {
+ /* Ensure we only read what in this record. */
+ dir_data_length = *next_offset;
+ }
+
+ if (dir_data_length < 105) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
+ finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
+ finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
+ finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
+ finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
+ finfo->attr = IVAL(dir_data + 56, 0);
+ finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
+ namelen = IVAL(dir_data + 60,0);
+ if (namelen > (dir_data_length - 104)) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ finfo->reparse_tag = IVAL(dir_data + 64, 0);
+ slen = CVAL(dir_data + 68, 0);
+ if (slen > 24) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->short_name,
+ dir_data + 70,
+ slen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->name,
+ dir_data + 104,
+ namelen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (finfo->name == NULL) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Given a filename - get its directory name
+********************************************************************/
+
+static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ char **parent,
+ const char **name)
+{
+ char *p;
+ ptrdiff_t len;
+
+ p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
+
+ if (p == NULL) {
+ if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
+ return false;
+ }
+ if (name) {
+ *name = dir;
+ }
+ return true;
+ }
+
+ len = p-dir;
+
+ if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
+ return false;
+ }
+ (*parent)[len] = '\0';
+
+ if (name) {
+ *name = p+1;
+ }
+ return true;
+}
+
+struct cli_smb2_list_dir_data {
+ uint8_t *data;
+ uint32_t length;
+};
+
+struct cli_smb2_list_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *mask;
+
+ uint16_t fnum;
+
+ NTSTATUS status;
+ struct cli_smb2_list_dir_data *response;
+ uint32_t offset;
+ unsigned int info_level;
+};
+
+static void cli_smb2_list_opened(struct tevent_req *subreq);
+static void cli_smb2_list_done(struct tevent_req *subreq);
+static void cli_smb2_list_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_list_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *pathname,
+ unsigned int info_level)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_list_state *state = NULL;
+ char *parent = NULL;
+ bool ok;
+ struct smb2_create_blobs *in_cblobs = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->status = NT_STATUS_OK;
+ state->info_level = info_level;
+
+ ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_have_posix(cli->conn) &&
+ info_level == SMB2_FIND_POSIX_INFORMATION)
+ {
+ NTSTATUS status;
+
+ /* The mode MUST be 0 when opening an existing file/dir, and
+ * will be ignored by the server.
+ */
+ uint8_t linear_mode[4] = { 0 };
+ DATA_BLOB blob = { .data=linear_mode,
+ .length=sizeof(linear_mode) };
+
+ in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
+ if (in_cblobs == NULL) {
+ return NULL;
+ }
+
+ status = smb2_create_blob_add(in_cblobs, in_cblobs,
+ SMB2_CREATE_TAG_POSIX, blob);
+ if (tevent_req_nterror(req, status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ parent, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
+ SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ in_cblobs); /* in_cblobs */
+ TALLOC_FREE(in_cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
+ return req;
+}
+
+static void cli_smb2_list_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * Make our caller get back to us via cli_smb2_list_recv(),
+ * triggering the smb2_query_directory_send()
+ */
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+}
+
+static void cli_smb2_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ struct cli_smb2_list_dir_data *response = NULL;
+
+ response = talloc(state, struct cli_smb2_list_dir_data);
+ if (tevent_req_nomem(response, req)) {
+ return;
+ }
+
+ state->status = smb2cli_query_directory_recv(
+ subreq, response, &response->data, &response->length);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(state->status)) {
+ state->response = response;
+ state->offset = 0;
+
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+ return;
+ }
+
+ TALLOC_FREE(response);
+
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
+}
+
+static void cli_smb2_list_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+/*
+ * Return the next finfo directory.
+ *
+ * This parses the blob returned from QUERY_DIRECTORY step by step. If
+ * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
+ * NT_STATUS_RETRY, which will then trigger the caller again when the
+ * QUERY_DIRECTORY has returned with another buffer. This way we
+ * guarantee that no asynchronous request is open after this call
+ * returns an entry, so that other synchronous requests can be issued
+ * on the same connection while the directory listing proceeds.
+ */
+NTSTATUS cli_smb2_list_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo)
+{
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ struct cli_smb2_list_dir_data *response = NULL;
+ struct file_info *finfo = NULL;
+ NTSTATUS status;
+ uint32_t next_offset = 0;
+ bool in_progress;
+
+ in_progress = tevent_req_is_in_progress(req);
+
+ if (!in_progress) {
+ if (!tevent_req_is_nterror(req, &status)) {
+ status = NT_STATUS_NO_MORE_FILES;
+ }
+ goto fail;
+ }
+
+ response = state->response;
+ if (response == NULL) {
+ struct tevent_req *subreq = NULL;
+ struct cli_state *cli = state->cli;
+ struct smb2_hnd *ph = NULL;
+ uint32_t max_trans, max_avail_len;
+ bool ok;
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ status = state->status;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ max_trans = smb2cli_conn_max_trans_size(cli->conn);
+ ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
+ if (ok) {
+ max_trans = MIN(max_trans, max_avail_len);
+ }
+
+ subreq = smb2cli_query_directory_send(
+ state, /* mem_ctx */
+ state->ev, /* ev */
+ cli->conn, /* conn */
+ cli->timeout, /* timeout_msec */
+ cli->smb2.session, /* session */
+ cli->smb2.tcon, /* tcon */
+ state->info_level, /* level */
+ 0, /* flags */
+ 0, /* file_index */
+ ph->fid_persistent, /* fid_persistent */
+ ph->fid_volatile, /* fid_volatile */
+ state->mask, /* mask */
+ max_trans); /* outbuf_len */
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_done, req);
+ return NT_STATUS_RETRY;
+ }
+
+ SMB_ASSERT(response->length > state->offset);
+
+ finfo = talloc_zero(mem_ctx, struct file_info);
+ if (finfo == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
+ status = parse_finfo_posix_info(
+ response->data + state->offset,
+ response->length - state->offset,
+ finfo,
+ &next_offset);
+ } else {
+ status = parse_finfo_id_both_directory_info(
+ response->data + state->offset,
+ response->length - state->offset,
+ finfo,
+ &next_offset);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /*
+ * parse_finfo_id_both_directory_info() checks for overflow,
+ * no need to check again here.
+ */
+ state->offset += next_offset;
+
+ if (next_offset == 0) {
+ TALLOC_FREE(state->response);
+ }
+
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+
+ *pfinfo = finfo;
+ return NT_STATUS_OK;
+
+fail:
+ TALLOC_FREE(finfo);
+ tevent_req_received(req);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a path info (basic level).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
+ const char *name,
+ SMB_STRUCT_STAT *sbuf,
+ uint32_t *attributes)
+{
+ NTSTATUS status;
+ struct smb_create_returns cr;
+ uint16_t fnum = 0xffff;
+ size_t namelen = strlen(name);
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ end in a '\' */
+ if (namelen > 0 && name[namelen-1] == '\\') {
+ char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
+ if (modname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ name = modname;
+ }
+
+ /* This is commonly used as a 'cd'. Try qpathinfo on
+ a directory handle first. */
+
+ status = cli_smb2_create_fnum(cli,
+ name,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL,
+ &fnum,
+ &cr,
+ NULL,
+ NULL);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ /* Maybe a file ? */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ 0, /* create_options */
+ NULL,
+ &fnum,
+ &cr,
+ NULL,
+ NULL);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /* Maybe a reparse point ? */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ NULL,
+ &fnum,
+ &cr,
+ NULL,
+ NULL);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = cli_smb2_close_fnum(cli, fnum);
+
+ ZERO_STRUCTP(sbuf);
+
+ sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
+ sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
+ sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
+ sbuf->st_ex_size = cr.end_of_file;
+ *attributes = cr.file_attributes;
+
+ return status;
+}
+
+struct cli_smb2_query_info_fnum_state {
+ DATA_BLOB outbuf;
+};
+
+static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_query_info_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ uint32_t in_max_output_length,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info,
+ uint32_t in_flags)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_query_info_fnum_state *state = NULL;
+ struct smb2_hnd *ph = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
+ if (req == NULL) {
+ return req;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_query_info_send(
+ state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ in_info_type,
+ in_info_class,
+ in_max_output_length,
+ in_input_buffer,
+ in_additional_info,
+ in_flags,
+ ph->fid_persistent,
+ ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
+ return req;
+}
+
+static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_query_info_fnum_state);
+ DATA_BLOB outbuf;
+ NTSTATUS status;
+
+ status = smb2cli_query_info_recv(subreq, state, &outbuf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * We have to dup the memory here because outbuf.data is not
+ * returned as a talloc object by smb2cli_query_info_recv.
+ * It's a pointer into the received buffer.
+ */
+ state->outbuf = data_blob_dup_talloc(state, outbuf);
+
+ if ((outbuf.length != 0) &&
+ tevent_req_nomem(state->outbuf.data, req)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_query_info_fnum_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
+{
+ struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_query_info_fnum_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *outbuf = (DATA_BLOB) {
+ .data = talloc_move(mem_ctx, &state->outbuf.data),
+ .length = state->outbuf.length,
+ };
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_query_info_fnum(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ uint32_t in_max_output_length,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info,
+ uint32_t in_flags,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *outbuf)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ bool ok;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_query_info_fnum_send(
+ frame,
+ ev,
+ cli,
+ fnum,
+ in_info_type,
+ in_info_class,
+ in_max_output_length,
+ in_input_buffer,
+ in_additional_info,
+ in_flags);
+ if (req == NULL) {
+ goto fail;
+ }
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ goto fail;
+ }
+ status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Helper function for pathname operations.
+***************************************************************/
+
+struct get_fnum_from_path_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *name;
+ uint32_t desired_access;
+ uint16_t fnum;
+};
+
+static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
+static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
+static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
+
+static struct tevent_req *get_fnum_from_path_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *name,
+ uint32_t desired_access)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct get_fnum_from_path_state *state = NULL;
+ size_t namelen = strlen(name);
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct get_fnum_from_path_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->name = name;
+ state->desired_access = desired_access;
+
+ /*
+ * SMB2 is pickier about pathnames. Ensure it doesn't end in a
+ * '\'
+ */
+ if (namelen > 0 && name[namelen-1] == '\\') {
+ state->name = talloc_strndup(state, name, namelen-1);
+ if (tevent_req_nomem(state->name, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ ev, /* ev */
+ cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
+ desired_access, /* desired_access */
+ 0, /* file_attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ 0, /* create_options */
+ NULL); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
+ return req;
+}
+
+static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct get_fnum_from_path_state *state = tevent_req_data(
+ req, struct get_fnum_from_path_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
+ /*
+ * Naive option to match our SMB1 code. Assume the
+ * symlink path that tripped us up was the last
+ * component and try again. Eventually we will have to
+ * deal with the returned path unprocessed component. JRA.
+ */
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ state->ev, /* ev */
+ state->cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
+ state->desired_access, /* desired_access */
+ 0, /* file_attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ NULL); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, get_fnum_from_path_opened_reparse, req);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ state->ev, /* ev */
+ state->cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
+ state->desired_access, /* desired_access */
+ 0, /* file_attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, get_fnum_from_path_opened_dir, req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct get_fnum_from_path_state *state = tevent_req_data(
+ req, struct get_fnum_from_path_state);
+ NTSTATUS status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
+{
+ /* Abstraction violation, but these two are just the same... */
+ get_fnum_from_path_opened_reparse(subreq);
+}
+
+static NTSTATUS get_fnum_from_path_recv(
+ struct tevent_req *req, uint16_t *pfnum)
+{
+ struct get_fnum_from_path_state *state = tevent_req_data(
+ req, struct get_fnum_from_path_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!tevent_req_is_nterror(req, &status)) {
+ *pfnum = state->fnum;
+ }
+ tevent_req_received(req);
+ return status;
+}
+
+static NTSTATUS get_fnum_from_path(struct cli_state *cli,
+ const char *name,
+ uint32_t desired_access,
+ uint16_t *pfnum)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = get_fnum_from_path_recv(req, pfnum);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_qpathinfo_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ uint16_t fnum;
+ uint16_t level;
+ uint32_t min_rdata;
+ uint32_t max_rdata;
+
+ NTSTATUS status;
+ DATA_BLOB out;
+};
+
+static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
+static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
+static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint16_t level,
+ uint32_t min_rdata,
+ uint32_t max_rdata)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_qpathinfo_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct cli_smb2_qpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->level = level;
+ state->min_rdata = min_rdata;
+ state->max_rdata = max_rdata;
+
+ subreq = get_fnum_from_path_send(state,
+ ev,
+ cli,
+ fname,
+ FILE_READ_ATTRIBUTES);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
+ return req;
+}
+
+static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+ NTSTATUS status;
+
+ status = get_fnum_from_path_recv(subreq, &state->fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_smb2_query_info_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 1, /* in_info_type */
+ state->level,
+ state->max_rdata,
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0); /* in_flags */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
+}
+
+static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+
+ state->status =
+ cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(state->status) &&
+ (state->out.length < state->min_rdata)) {
+ state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
+}
+
+static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+ NTSTATUS status;
+
+ status = cli_smb2_close_fnum_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (tevent_req_nterror(req, state->status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **rdata,
+ uint32_t *num_rdata)
+{
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *rdata = talloc_move(mem_ctx, &state->out.data);
+ *num_rdata = state->out.length;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
+ a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
+ const char *name,
+ uint8_t in_info_type,
+ uint8_t in_file_info_class,
+ const DATA_BLOB *p_in_data)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_WRITE_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = cli_smb2_set_info_fnum(
+ cli,
+ fnum,
+ in_info_type,
+ in_file_info_class,
+ p_in_data, /* in_input_buffer */
+ 0); /* in_additional_info */
+ fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to set pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+ const char *name,
+ uint32_t attr,
+ time_t mtime)
+{
+ uint8_t inbuf_store[40];
+ DATA_BLOB inbuf = data_blob_null;
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+ inbuf.data = inbuf_store;
+ inbuf.length = sizeof(inbuf_store);
+ data_blob_clear(&inbuf);
+
+ /*
+ * SMB1 uses attr == 0 to clear all attributes
+ * on a file (end up with FILE_ATTRIBUTE_NORMAL),
+ * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
+ * request attribute change.
+ *
+ * SMB2 uses exactly the reverse. Unfortunately as the
+ * cli_setatr() ABI is exposed inside libsmbclient,
+ * we must make the SMB2 cli_smb2_setatr() call
+ * export the same ABI as the SMB1 cli_setatr()
+ * which calls it. This means reversing the sense
+ * of the requested attr argument if it's zero
+ * or FILE_ATTRIBUTE_NORMAL.
+ *
+ * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
+ */
+
+ if (attr == 0) {
+ attr = FILE_ATTRIBUTE_NORMAL;
+ } else if (attr == FILE_ATTRIBUTE_NORMAL) {
+ attr = 0;
+ }
+
+ SIVAL(inbuf.data, 32, attr);
+ if (mtime != 0) {
+ put_long_date((char *)inbuf.data + 16,mtime);
+ }
+ /* Set all the other times to -1. */
+ SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+ SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
+ SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
+
+ return cli_smb2_setpathinfo(cli,
+ name,
+ 1, /* in_info_type */
+ /* in_file_info_class */
+ SMB_FILE_BASIC_INFORMATION - 1000,
+ &inbuf);
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to set file handle times.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time)
+{
+ uint8_t inbuf_store[40];
+ DATA_BLOB inbuf = data_blob_null;
+ NTSTATUS status;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+ inbuf.data = inbuf_store;
+ inbuf.length = sizeof(inbuf_store);
+ data_blob_clear(&inbuf);
+
+ SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+ if (change_time != 0) {
+ put_long_date((char *)inbuf.data + 24, change_time);
+ }
+ if (access_time != 0) {
+ put_long_date((char *)inbuf.data + 8, access_time);
+ }
+ if (write_time != 0) {
+ put_long_date((char *)inbuf.data + 16, write_time);
+ }
+
+ status = cli_smb2_set_info_fnum(cli,
+ fnum,
+ 1, /* in_info_type */
+ SMB_FILE_BASIC_INFORMATION -
+ 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+ cli->raw_status = status;
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query disk attributes (size).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
+ uint64_t *bsize, uint64_t *total, uint64_t *avail)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
+ uint32_t sectors_per_unit = 0;
+ uint32_t bytes_per_sector = 0;
+ uint64_t total_size = 0;
+ uint64_t size_free = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* First open the top level directory. */
+ status = cli_smb2_create_fnum(cli,
+ path,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL,
+ &fnum,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 3 (SMB_FS_SIZE_INFORMATION). */
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ fnum,
+ 2, /* in_info_type */
+ 3, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ if (outbuf.length != 24) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ total_size = BVAL(outbuf.data, 0);
+ size_free = BVAL(outbuf.data, 8);
+ sectors_per_unit = IVAL(outbuf.data, 16);
+ bytes_per_sector = IVAL(outbuf.data, 20);
+
+ if (bsize) {
+ *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
+ }
+ if (total) {
+ *total = total_size;
+ }
+ if (avail) {
+ *avail = size_free;
+ }
+
+ status = NT_STATUS_OK;
+
+ fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query file system sizes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
+ uint64_t *total_allocation_units,
+ uint64_t *caller_allocation_units,
+ uint64_t *actual_allocation_units,
+ uint64_t *sectors_per_allocation_unit,
+ uint64_t *bytes_per_sector)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL,
+ &fnum,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ fnum,
+ SMB2_0_INFO_FILESYSTEM, /* in_info_type */
+ SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (outbuf.length < 32) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ *total_allocation_units = BIG_UINT(outbuf.data, 0);
+ *caller_allocation_units = BIG_UINT(outbuf.data, 8);
+ *actual_allocation_units = BIG_UINT(outbuf.data, 16);
+ *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
+ *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
+
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query file system attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL,
+ &fnum,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ fnum,
+ 2, /* in_info_type */
+ 5, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (outbuf.length < 12) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ *fs_attr = IVAL(outbuf.data, 0);
+
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query file system volume info.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ char **_volume_name,
+ uint32_t *pserial_number,
+ time_t *pdate)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
+ uint32_t nlen;
+ char *volume_name = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* First open the top level directory. */
+ status =
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ NULL,
+ &fnum,
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 1 (SMB_FS_VOLUME_INFORMATION). */
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ fnum,
+ SMB2_0_INFO_FILESYSTEM, /* in_info_type */
+ /* in_file_info_class */
+ SMB_FS_VOLUME_INFORMATION - 1000,
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (outbuf.length < 24) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ if (pdate) {
+ struct timespec ts;
+ ts = interpret_long_date(BVAL(outbuf.data, 0));
+ *pdate = ts.tv_sec;
+ }
+ if (pserial_number) {
+ *pserial_number = IVAL(outbuf.data,8);
+ }
+ nlen = IVAL(outbuf.data,12);
+ if (nlen + 18 < 18) {
+ /* Integer wrap. */
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+ /*
+ * The next check is safe as we know outbuf.length >= 24
+ * from above.
+ */
+ if (nlen > (outbuf.length - 18)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ pull_string_talloc(mem_ctx,
+ (const char *)outbuf.data,
+ 0,
+ &volume_name,
+ outbuf.data + 18,
+ nlen,
+ STR_UNICODE);
+ if (volume_name == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+
+ *_volume_name = volume_name;
+
+fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_mxac_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ struct smb2_create_blobs in_cblobs;
+ uint16_t fnum;
+ NTSTATUS status;
+ uint32_t mxac;
+};
+
+static void cli_smb2_mxac_opened(struct tevent_req *subreq);
+static void cli_smb2_mxac_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_mxac_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ *state = (struct cli_smb2_mxac_state) {
+ .ev = ev,
+ .cli = cli,
+ .fname = fname,
+ };
+
+ status = smb2_create_blob_add(state,
+ &state->in_cblobs,
+ SMB2_CREATE_TAG_MXAC,
+ data_blob(NULL, 0));
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb2_create_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fname,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES,
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN,
+ 0, /* create_options */
+ &state->in_cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
+ return req;
+}
+
+static void cli_smb2_mxac_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_mxac_state *state = tevent_req_data(
+ req, struct cli_smb2_mxac_state);
+ struct smb2_create_blobs out_cblobs = {0};
+ struct smb2_create_blob *mxac_blob = NULL;
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
+ if (mxac_blob == NULL) {
+ state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto close;
+ }
+ if (mxac_blob->data.length != 8) {
+ state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto close;
+ }
+
+ state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
+ state->mxac = IVAL(mxac_blob->data.data, 4);
+
+close:
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
+
+ return;
+}
+
+static void cli_smb2_mxac_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb2_close_fnum_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
+{
+ struct cli_smb2_mxac_state *state = tevent_req_data(
+ req, struct cli_smb2_mxac_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ return state->status;
+ }
+
+ *mxac = state->mxac;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
+ const char *fname,
+ uint32_t *_mxac)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+ bool ok;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ goto fail;
+ }
+ status = cli_smb2_query_mxac_recv(req, _mxac);
+
+fail:
+ cli->raw_status = status;
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_rename_fnum_state {
+ DATA_BLOB inbuf;
+};
+
+static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_rename_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ const char *fname_dst,
+ bool replace)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_rename_fnum_state *state = NULL;
+ size_t namelen = strlen(fname_dst);
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ size_t inbuf_size;
+ bool ok;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_rename_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * SMB2 is pickier about pathnames. Ensure it doesn't start in
+ * a '\'
+ */
+ if (*fname_dst == '\\') {
+ fname_dst++;
+ }
+
+ /*
+ * SMB2 is pickier about pathnames. Ensure it doesn't end in a
+ * '\'
+ */
+ if (namelen > 0 && fname_dst[namelen-1] == '\\') {
+ fname_dst = talloc_strndup(state, fname_dst, namelen-1);
+ if (tevent_req_nomem(fname_dst, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ ok = push_ucs2_talloc(
+ state, &converted_str, fname_dst, &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * W2K8 insists the dest name is not null terminated. Remove
+ * the last 2 zero bytes and reduce the name length.
+ */
+ if (converted_size_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ inbuf_size = 20 + converted_size_bytes;
+ if (inbuf_size < 20) {
+ /* Integer wrap check. */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * The Windows 10 SMB2 server has a minimum length
+ * for a SMB2_FILE_RENAME_INFORMATION buffer of
+ * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
+ * if the length is less. This isn't an alignment
+ * issue as Windows client accepts happily 2-byte align
+ * for larger target name sizes. Also the Windows 10
+ * SMB1 server doesn't have this restriction.
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
+ */
+ inbuf_size = MAX(inbuf_size, 24);
+
+ state->inbuf = data_blob_talloc_zero(state, inbuf_size);
+ if (tevent_req_nomem(state->inbuf.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (replace) {
+ SCVAL(state->inbuf.data, 0, 1);
+ }
+
+ SIVAL(state->inbuf.data, 16, converted_size_bytes);
+ memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
+
+ TALLOC_FREE(converted_str);
+
+ /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
+ level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+
+ subreq = cli_smb2_set_info_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 1, /* in_info_type */
+ SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
+ &state->inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
+ return req;
+}
+
+static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to rename a file.
+***************************************************************/
+
+struct cli_smb2_rename_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname_dst;
+ bool replace;
+ uint16_t fnum;
+
+ NTSTATUS rename_status;
+};
+
+static void cli_smb2_rename_opened(struct tevent_req *subreq);
+static void cli_smb2_rename_renamed(struct tevent_req *subreq);
+static void cli_smb2_rename_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_rename_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_rename_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_rename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Strip a MSDFS path from fname_dst if we were given one.
+ */
+ status = cli_dfs_target_check(state,
+ cli,
+ fname_dst,
+ &fname_dst);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->ev = ev;
+ state->cli = cli;
+ state->fname_dst = fname_dst;
+ state->replace = replace;
+
+ subreq = get_fnum_from_path_send(
+ state, ev, cli, fname_src, DELETE_ACCESS);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
+ return req;
+}
+
+static void cli_smb2_rename_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
+ NTSTATUS status;
+
+ status = get_fnum_from_path_recv(subreq, &state->fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_smb2_rename_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ state->fname_dst,
+ state->replace);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
+}
+
+static void cli_smb2_rename_renamed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
+
+ state->rename_status = cli_smb2_rename_fnum_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
+}
+
+static void cli_smb2_rename_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
+{
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (!tevent_req_is_nterror(req, &status)) {
+ status = state->rename_status;
+ }
+ tevent_req_received(req);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a fnum.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
+ uint16_t fnum,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ size_t bloblen = 0;
+ char *ea_name_ascii = NULL;
+ size_t namelen = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* Marshall the SMB2 EA data. */
+ if (ea_len > 0xFFFF) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (!push_ascii_talloc(frame,
+ &ea_name_ascii,
+ ea_name,
+ &namelen)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (namelen < 2 || namelen > 0xFF) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ bloblen = 8 + ea_len + namelen;
+ /* Round up to a 4 byte boundary. */
+ bloblen = ((bloblen + 3)&~3);
+
+ inbuf = data_blob_talloc_zero(frame, bloblen);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ /* namelen doesn't include the NULL byte. */
+ SCVAL(inbuf.data, 5, namelen - 1);
+ SSVAL(inbuf.data, 6, ea_len);
+ memcpy(inbuf.data + 8, ea_name_ascii, namelen);
+ memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+
+ status = cli_smb2_set_info_fnum(
+ cli,
+ fnum,
+ 1, /* in_info_type */
+ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+
+ fail:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
+ const char *name,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_WRITE_EA,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = cli_set_ea_fnum(cli,
+ fnum,
+ ea_name,
+ ea_val,
+ ea_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get an EA list on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
+ const char *name,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_array)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0xffff;
+ DATA_BLOB outbuf = data_blob_null;
+ struct ea_list *ea_list = NULL;
+ struct ea_list *eal = NULL;
+ size_t ea_count = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ *pnum_eas = 0;
+ *pea_array = NULL;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_EA,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+ level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ fnum,
+ 1, /* in_info_type */
+ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ ea_list = read_nttrans_ea_list(ctx,
+ (const char *)outbuf.data,
+ outbuf.length);
+ if (ea_list == NULL) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ /* Convert to an array. */
+ for (eal = ea_list; eal; eal = eal->next) {
+ ea_count++;
+ }
+
+ if (ea_count) {
+ *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
+ if (*pea_array == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ ea_count = 0;
+ for (eal = ea_list; eal; eal = eal->next) {
+ (*pea_array)[ea_count++] = eal->ea;
+ }
+ *pnum_eas = ea_count;
+ }
+
+ fail:
+
+ if (fnum != 0xffff) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ DATA_BLOB info_blob = data_blob_null;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+ unsigned sid_len;
+ unsigned int offset;
+ struct smb2_query_quota_info query = {0};
+ struct file_get_quota_info info = {0};
+ enum ndr_err_code err;
+ struct ndr_push *ndr_push = NULL;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+
+ query.return_single = 1;
+
+ info.next_entry_offset = 0;
+ info.sid_length = sid_len;
+ info.sid = pqt->sid;
+
+ err = ndr_push_struct_blob(
+ &info_blob,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ query.sid_list_length = info_blob.length;
+ ndr_push = ndr_push_init_ctx(frame);
+ if (!ndr_push) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ err = ndr_push_smb2_query_quota_info(ndr_push,
+ NDR_SCALARS | NDR_BUFFERS,
+ &query);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
+ info_blob.length);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+ inbuf.data = ndr_push->data;
+ inbuf.length = ndr_push->offset;
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ quota_fnum,
+ 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
+ pqt)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
+ }
+
+fail:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to list user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list,
+ bool first)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct smb2_query_quota_info info = {0};
+ enum ndr_err_code err;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ info.restart_scan = first ? 1 : 0;
+
+ err = ndr_push_struct_blob(
+ &inbuf,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto cleanup;
+ }
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ quota_fnum,
+ 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+
+ /*
+ * safeguard against panic from calling parse_user_quota_list with
+ * NULL buffer
+ */
+ if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
+ pqt_list);
+
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get file system quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = cli_smb2_query_info_fnum(
+ cli,
+ quota_fnum,
+ 2, /* in_info_type */
+ SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
+
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST *qtl)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = cli_smb2_set_info_fnum(
+ cli,
+ quota_fnum,
+ 4, /* in_info_type */
+ 0, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+cleanup:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = cli_smb2_set_info_fnum(
+ cli,
+ quota_fnum,
+ 2, /* in_info_type */
+ SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+cleanup:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_read_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint64_t start_offset;
+ uint32_t size;
+ uint32_t received;
+ uint8_t *buf;
+};
+
+static void cli_smb2_read_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->start_offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->received = 0;
+ state->buf = NULL;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_read_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->size,
+ state->start_offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* minimum_count */
+ 0); /* remaining_bytes */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_read_done, req);
+ return req;
+}
+
+static void cli_smb2_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_read_state *state = tevent_req_data(
+ req, struct cli_smb2_read_state);
+ NTSTATUS status;
+
+ status = smb2cli_read_recv(subreq, state,
+ &state->buf, &state->received);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->received > state->size) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
+ ssize_t *received,
+ uint8_t **rcvbuf)
+{
+ NTSTATUS status;
+ struct cli_smb2_read_state *state = tevent_req_data(
+ req, struct cli_smb2_read_state);
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ return status;
+ }
+ /*
+ * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
+ * better make sure that you copy it away before you talloc_free(req).
+ * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
+ */
+ *received = (ssize_t)state->received;
+ *rcvbuf = state->buf;
+ state->cli->raw_status = NT_STATUS_OK;
+ return NT_STATUS_OK;
+}
+
+struct cli_smb2_write_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint32_t flags;
+ const uint8_t *buf;
+ uint64_t offset;
+ uint32_t size;
+ uint32_t written;
+};
+
+static void cli_smb2_write_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq = NULL;
+ struct cli_smb2_write_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+ state->flags = (uint32_t)mode;
+ state->buf = buf;
+ state->offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->written = 0;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->size,
+ state->offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_write_written, req);
+ return req;
+}
+
+static void cli_smb2_write_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_write_state *state = tevent_req_data(
+ req, struct cli_smb2_write_state);
+ NTSTATUS status;
+ uint32_t written;
+
+ status = smb2cli_write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->written = written;
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
+ size_t *pwritten)
+{
+ struct cli_smb2_write_state *state = tevent_req_data(
+ req, struct cli_smb2_write_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (pwritten != NULL) {
+ *pwritten = (size_t)state->written;
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 async write using an fnum.
+ This is mostly cut-and-paste from Volker's code inside
+ source3/libsmb/clireadwrite.c, adapted for SMB2.
+
+ Done this way so I can reuse all the logic inside cli_push()
+ for free :-).
+***************************************************************/
+
+struct cli_smb2_writeall_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint32_t flags;
+ const uint8_t *buf;
+ uint64_t offset;
+ uint32_t size;
+ uint32_t written;
+};
+
+static void cli_smb2_writeall_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq = NULL;
+ struct cli_smb2_writeall_state *state = NULL;
+ uint32_t to_write;
+ uint32_t max_size;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+ state->flags = (uint32_t)mode;
+ state->buf = buf;
+ state->offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->written = 0;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ to_write = state->size;
+ max_size = smb2cli_conn_max_write_size(state->cli->conn);
+ to_write = MIN(max_size, to_write);
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (ok) {
+ to_write = MIN(max_size, to_write);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ to_write,
+ state->offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf + state->written);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+ return req;
+}
+
+static void cli_smb2_writeall_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_writeall_state *state = tevent_req_data(
+ req, struct cli_smb2_writeall_state);
+ NTSTATUS status;
+ uint32_t written, to_write;
+ uint32_t max_size;
+ bool ok;
+
+ status = smb2cli_write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->written += written;
+
+ if (state->written > state->size) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ to_write = state->size - state->written;
+
+ if (to_write == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ max_size = smb2cli_conn_max_write_size(state->cli->conn);
+ to_write = MIN(max_size, to_write);
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (ok) {
+ to_write = MIN(max_size, to_write);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ to_write,
+ state->offset + state->written,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf + state->written);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+}
+
+NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+ size_t *pwritten)
+{
+ struct cli_smb2_writeall_state *state = tevent_req_data(
+ req, struct cli_smb2_writeall_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ return status;
+ }
+ if (pwritten != NULL) {
+ *pwritten = (size_t)state->written;
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ return NT_STATUS_OK;
+}
+
+struct cli_smb2_splice_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *src_ph;
+ struct smb2_hnd *dst_ph;
+ int (*splice_cb)(off_t n, void *priv);
+ void *priv;
+ off_t written;
+ off_t size;
+ off_t src_offset;
+ off_t dst_offset;
+ bool resized;
+ struct req_resume_key_rsp resume_rsp;
+ struct srv_copychunk_copy cc_copy;
+};
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req);
+
+static void cli_splice_copychunk_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ struct smbXcli_conn *conn = state->cli->conn;
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+ struct srv_copychunk_rsp cc_copy_rsp;
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
+ state->resized) && tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
+ cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
+ if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
+ max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
+ tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->resized = true;
+ smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
+ smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
+ } else {
+ if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ state->src_offset += cc_copy_rsp.total_bytes_written;
+ state->dst_offset += cc_copy_rsp.total_bytes_written;
+ state->written += cc_copy_rsp.total_bytes_written;
+ if (!state->splice_cb(state->written, state->priv)) {
+ tevent_req_nterror(req, NT_STATUS_CANCELLED);
+ return;
+ }
+ }
+
+ cli_splice_copychunk_send(state, req);
+}
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ enum ndr_err_code ndr_ret;
+ struct smbXcli_conn *conn = state->cli->conn;
+ struct srv_copychunk_copy *cc_copy = &state->cc_copy;
+ off_t src_offset = state->src_offset;
+ off_t dst_offset = state->dst_offset;
+ uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
+ state->size - state->written);
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ if (state->size - state->written == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ cc_copy->chunk_count = 0;
+ while (req_len) {
+ cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
+ cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
+ cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
+ smb2cli_conn_cc_chunk_len(conn));
+ if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
+ if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
+ (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ cc_copy->chunk_count++;
+ }
+
+ ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to marshall copy chunk req\n"));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->dst_ph->fid_persistent, /* in_fid_persistent */
+ state->dst_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_COPYCHUNK_WRITE,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 12, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_copychunk_done,
+ req);
+}
+
+static void cli_splice_key_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
+ state, &state->resume_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall resume key rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ memcpy(&state->cc_copy.source_key,
+ &state->resume_rsp.resume_key,
+ sizeof state->resume_rsp.resume_key);
+
+ cli_splice_copychunk_send(state, req);
+}
+
+struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size, off_t src_offset, off_t dst_offset,
+ int (*splice_cb)(off_t n, void *priv),
+ void *priv)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct cli_smb2_splice_state *state;
+ NTSTATUS status;
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->ev = ev;
+ state->splice_cb = splice_cb;
+ state->priv = priv;
+ state->size = size;
+ state->written = 0;
+ state->src_offset = src_offset;
+ state->dst_offset = dst_offset;
+ state->cc_copy.chunks = talloc_array(state,
+ struct srv_copychunk,
+ smb2cli_conn_cc_max_chunks(cli->conn));
+ if (state->cc_copy.chunks == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ subreq = smb2cli_ioctl_send(state, ev, cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ state->src_ph->fid_persistent, /* in_fid_persistent */
+ state->src_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_REQUEST_RESUME_KEY,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 32, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return NULL;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_key_done,
+ req);
+
+ return req;
+}
+
+NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
+{
+ struct cli_smb2_splice_state *state = tevent_req_data(
+ req, struct cli_smb2_splice_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->cli->raw_status = status;
+ tevent_req_received(req);
+ return status;
+ }
+ if (written != NULL) {
+ *written = state->written;
+ }
+ state->cli->raw_status = NT_STATUS_OK;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ SMB2 enum shadow copy data.
+***************************************************************/
+
+struct cli_smb2_shadow_copy_data_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ DATA_BLOB out_input_buffer;
+ DATA_BLOB out_output_buffer;
+};
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_shadow_copy_data_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_shadow_copy_data_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO. Under SMB2 we should send a zero max_output_length
+ * ioctl to get the required size, then send another ioctl
+ * to get the data, but the current SMB1 implementation just
+ * does one roundtrip with a 64K buffer size. Do the same
+ * for now. JRA.
+ */
+
+ subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->ph->fid_persistent, /* in_fid_persistent */
+ state->ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_GET_SHADOW_COPY_DATA,
+ 0, /* in_max_input_length */
+ NULL, /* in_input_buffer */
+ get_names ?
+ CLI_BUFFER_SIZE : 16, /* in_max_output_length */
+ NULL, /* in_output_buffer */
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_shadow_copy_data_fnum_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &state->out_input_buffer,
+ &state->out_output_buffer);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ char **names = NULL;
+ uint32_t num_names = 0;
+ uint32_t num_names_returned = 0;
+ uint32_t dlength = 0;
+ uint32_t i;
+ uint8_t *endp = NULL;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->out_output_buffer.length < 16) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ num_names = IVAL(state->out_output_buffer.data, 0);
+ num_names_returned = IVAL(state->out_output_buffer.data, 4);
+ dlength = IVAL(state->out_output_buffer.data, 8);
+
+ if (num_names > 0x7FFFFFFF) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (get_names == false) {
+ *pnum_names = (int)num_names;
+ return NT_STATUS_OK;
+ }
+ if (num_names != num_names_returned) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (dlength + 12 < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ /*
+ * NB. The below is an allowable return if there are
+ * more snapshots than the buffer size we told the
+ * server we can receive. We currently don't support
+ * this.
+ */
+ if (dlength + 12 > state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (state->out_output_buffer.length +
+ (2 * sizeof(SHADOW_COPY_LABEL)) <
+ state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ names = talloc_array(mem_ctx, char *, num_names_returned);
+ if (names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ endp = state->out_output_buffer.data +
+ state->out_output_buffer.length;
+
+ for (i=0; i<num_names_returned; i++) {
+ bool ret;
+ uint8_t *src;
+ size_t converted_size;
+
+ src = state->out_output_buffer.data + 12 +
+ (i * 2 * sizeof(SHADOW_COPY_LABEL));
+
+ if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ ret = convert_string_talloc(
+ names, CH_UTF16LE, CH_UNIX,
+ src, 2 * sizeof(SHADOW_COPY_LABEL),
+ &names[i], &converted_size);
+ if (!ret) {
+ TALLOC_FREE(names);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *pnum_names = num_names;
+ *pnames = names;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_shadow_copy_data_fnum_send(frame,
+ ev,
+ cli,
+ fnum,
+ get_names);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_shadow_copy_data_fnum_recv(req,
+ mem_ctx,
+ get_names,
+ pnames,
+ pnum_names);
+ fail:
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to truncate a file.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t newsize)
+{
+ NTSTATUS status;
+ uint8_t buf[8] = {0};
+ DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ SBVAL(buf, 0, newsize);
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
+
+ status = cli_smb2_set_info_fnum(
+ cli,
+ fnum,
+ 1, /* in_info_type */
+ SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0);
+
+ fail:
+
+ cli->raw_status = status;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_notify_state {
+ struct tevent_req *subreq;
+ struct notify_change *changes;
+ size_t num_changes;
+};
+
+static void cli_smb2_notify_done(struct tevent_req *subreq);
+static bool cli_smb2_notify_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_notify_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t buffer_size,
+ uint32_t completion_filter,
+ bool recursive)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb2_notify_state *state = NULL;
+ struct smb2_hnd *ph = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_notify_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->subreq = smb2cli_notify_send(
+ state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ buffer_size,
+ ph->fid_persistent,
+ ph->fid_volatile,
+ completion_filter,
+ recursive);
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
+ tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
+ return req;
+}
+
+static bool cli_smb2_notify_cancel(struct tevent_req *req)
+{
+ struct cli_smb2_notify_state *state = tevent_req_data(
+ req, struct cli_smb2_notify_state);
+ bool ok;
+
+ ok = tevent_req_cancel(state->subreq);
+ return ok;
+}
+
+static void cli_smb2_notify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_notify_state *state = tevent_req_data(
+ req, struct cli_smb2_notify_state);
+ uint8_t *base;
+ uint32_t len;
+ uint32_t ofs;
+ NTSTATUS status;
+
+ status = smb2cli_notify_recv(subreq, state, &base, &len);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ tevent_req_done(req);
+ return;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ofs = 0;
+
+ while (len - ofs >= 12) {
+ struct notify_change *tmp;
+ struct notify_change *c;
+ uint32_t next_ofs = IVAL(base, ofs);
+ uint32_t file_name_length = IVAL(base, ofs+8);
+ size_t namelen;
+ bool ok;
+
+ tmp = talloc_realloc(
+ state,
+ state->changes,
+ struct notify_change,
+ state->num_changes + 1);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->changes = tmp;
+ c = &state->changes[state->num_changes];
+ state->num_changes += 1;
+
+ if (smb_buffer_oob(len, ofs, next_ofs) ||
+ smb_buffer_oob(len, ofs+12, file_name_length)) {
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ c->action = IVAL(base, ofs+4);
+
+ ok = convert_string_talloc(
+ state->changes,
+ CH_UTF16LE,
+ CH_UNIX,
+ base + ofs + 12,
+ file_name_length,
+ &c->name,
+ &namelen);
+ if (!ok) {
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (next_ofs == 0) {
+ break;
+ }
+ ofs += next_ofs;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct notify_change **pchanges,
+ uint32_t *pnum_changes)
+{
+ struct cli_smb2_notify_state *state = tevent_req_data(
+ req, struct cli_smb2_notify_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pchanges = talloc_move(mem_ctx, &state->changes);
+ *pnum_changes = state->num_changes;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
+ uint32_t buffer_size, uint32_t completion_filter,
+ bool recursive, TALLOC_CTX *mem_ctx,
+ struct notify_change **pchanges,
+ uint32_t *pnum_changes)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_notify_send(
+ frame,
+ ev,
+ cli,
+ fnum,
+ buffer_size,
+ completion_filter,
+ recursive);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_fsctl_state {
+ DATA_BLOB out;
+};
+
+static void cli_smb2_fsctl_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_fsctl_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t ctl_code,
+ const DATA_BLOB *in,
+ uint32_t max_out)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_fsctl_state *state = NULL;
+ struct smb2_hnd *ph = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_ioctl_send(
+ state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ ph->fid_persistent,
+ ph->fid_volatile,
+ ctl_code,
+ 0, /* in_max_input_length */
+ in,
+ max_out,
+ NULL,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
+ return req;
+}
+
+static void cli_smb2_fsctl_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_fsctl_state *state = tevent_req_data(
+ req, struct cli_smb2_fsctl_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_fsctl_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
+{
+ struct cli_smb2_fsctl_state *state = tevent_req_data(
+ req, struct cli_smb2_fsctl_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (state->out.length == 0) {
+ *out = (DATA_BLOB) { .data = NULL, };
+ } else {
+ /*
+ * Can't use talloc_move() here, the outblobs from
+ * smb2cli_ioctl_recv() are not standalone talloc
+ * objects but just peek into the larger buffers
+ * received, hanging off "state".
+ */
+ *out = data_blob_talloc(
+ mem_ctx, state->out.data, state->out.length);
+ if (out->data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
new file mode 100644
index 0000000..abac569
--- /dev/null
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -0,0 +1,324 @@
+/*
+ Unix SMB/CIFS implementation.
+ smb2 wrapper client routines
+ Copyright (C) Jeremy Allison 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SMB2CLI_FNUM_H__
+#define __SMB2CLI_FNUM_H__
+
+struct smbXcli_conn;
+struct smbXcli_session;
+struct cli_state;
+struct file_info;
+struct symlink_reparse_struct;
+
+struct cli_smb2_create_flags {
+ bool batch_oplock:1;
+ bool exclusive_oplock:1;
+};
+
+struct tevent_req *cli_smb2_create_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ struct cli_smb2_create_flags create_flags,
+ uint32_t impersonation_level,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ const struct smb2_create_blobs *in_cblobs);
+NTSTATUS cli_smb2_create_fnum_recv(
+ struct tevent_req *req,
+ uint16_t *pfnum,
+ struct smb_create_returns *cr,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_create_blobs *out_cblobs,
+ struct symlink_reparse_struct **symlink);
+NTSTATUS cli_smb2_create_fnum(
+ struct cli_state *cli,
+ const char *fname,
+ struct cli_smb2_create_flags create_flags,
+ uint32_t impersonation_level,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ const struct smb2_create_blobs *in_cblobs,
+ uint16_t *pfid,
+ struct smb_create_returns *cr,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_create_blobs *out_cblobs);
+
+struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t flags);
+NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req);
+NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum);
+struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool flag);
+NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req);
+NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag);
+struct tevent_req *cli_smb2_mkdir_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *name);
+NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req);
+struct tevent_req *cli_smb2_rmdir_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname,
+ const struct smb2_create_blobs *in_cblobs);
+NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req);
+struct tevent_req *cli_smb2_unlink_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ const struct smb2_create_blobs *in_cblobs);
+NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req);
+struct tevent_req *cli_smb2_list_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *pathname,
+ unsigned int info_level);
+NTSTATUS cli_smb2_list_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo);
+NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
+ const char *name,
+ SMB_STRUCT_STAT *sbuf,
+ uint32_t *attributes);
+struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint16_t level,
+ uint32_t min_rdata,
+ uint32_t max_rdata);
+NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **rdata,
+ uint32_t *num_rdata);
+struct tevent_req *cli_smb2_query_info_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ uint32_t in_max_output_length,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info,
+ uint32_t in_flags);
+NTSTATUS cli_smb2_query_info_fnum_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf);
+struct tevent_req *cli_smb2_set_info_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info);
+NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req);
+NTSTATUS cli_smb2_set_info_fnum(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info);
+NTSTATUS cli_smb2_query_info_fnum(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t in_info_type,
+ uint8_t in_info_class,
+ uint32_t in_max_output_length,
+ const DATA_BLOB *in_input_buffer,
+ uint32_t in_additional_info,
+ uint32_t in_flags,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *outbuf);
+NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
+ const char *name,
+ uint8_t in_info_type,
+ uint8_t in_file_info_class,
+ const DATA_BLOB *p_in_data);
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+ const char *fname,
+ uint32_t attr,
+ time_t mtime);
+NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time);
+NTSTATUS cli_smb2_dskattr(struct cli_state *cli,
+ const char *path,
+ uint64_t *bsize,
+ uint64_t *total,
+ uint64_t *avail);
+NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr);
+NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
+ uint64_t *total_allocation_units,
+ uint64_t *caller_allocation_units,
+ uint64_t *actual_allocation_units,
+ uint64_t *sectors_per_allocation_unit,
+ uint64_t *bytes_per_sector);
+NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ char **_volume_name,
+ uint32_t *pserial_number,
+ time_t *pdate);
+struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req,
+ uint32_t *mxac);
+NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
+ const char *fname,
+ uint32_t *mxac);
+struct tevent_req *cli_smb2_rename_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace);
+NTSTATUS cli_smb2_rename_recv(struct tevent_req *req);
+NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
+ uint16_t fnum,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len);
+NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
+ const char *name,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list);
+NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
+ const char *name,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len);
+NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list,
+ bool first);
+NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST *qtl);
+NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ off_t offset,
+ size_t size);
+NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
+ ssize_t *received,
+ uint8_t **rcvbuf);
+struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size);
+NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
+ size_t *pwritten);
+struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size);
+NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+ size_t *pwritten);
+struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size, off_t src_offset, off_t dst_offset,
+ int (*splice_cb)(off_t n, void *priv), void *priv);
+NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written);
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names);
+NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t newsize);
+struct tevent_req *cli_smb2_notify_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t buffer_size,
+ uint32_t completion_filter,
+ bool recursive);
+NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct notify_change **pchanges,
+ uint32_t *pnum_changes);
+NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
+ uint32_t buffer_size, uint32_t completion_filter,
+ bool recursive, TALLOC_CTX *mem_ctx,
+ struct notify_change **pchanges,
+ uint32_t *pnum_changes);
+
+struct tevent_req *cli_smb2_fsctl_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t ctl_code,
+ const DATA_BLOB *in,
+ uint32_t max_out);
+NTSTATUS cli_smb2_fsctl_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out);
+
+#endif /* __SMB2CLI_FNUM_H__ */
diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
new file mode 100644
index 0000000..61e3504
--- /dev/null
+++ b/source3/libsmb/cliconnect.c
@@ -0,0 +1,4026 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Volker Lendecke 2011
+ Copyright (C) Jeremy Allison 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/namequery.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../libcli/auth/spnego.h"
+#include "smb_krb5.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth_generic.h"
+#include "libads/kerberos_proto.h"
+#include "krb5_env.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/nmblib.h"
+#include "librpc/ndr/libndr.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../libcli/smb/smb_seal.h"
+#include "lib/param/param.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
+
+#define STAR_SMBSERVER "*SMBSERVER"
+
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+ const char *principal);
+
+struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *domain,
+ const char *realm,
+ const char *password,
+ bool use_kerberos,
+ bool fallback_after_kerberos,
+ bool use_ccache,
+ bool password_is_nt_hash)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ const char *principal = NULL;
+ char *tmp = NULL;
+ char *p = NULL;
+ bool ok;
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ goto fail;
+ }
+ ok = cli_credentials_set_conf(creds, lp_ctx);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (username == NULL) {
+ username = "";
+ }
+
+ if (strlen(username) == 0) {
+ if (password != NULL && strlen(password) == 0) {
+ /*
+ * some callers pass "" as no password
+ *
+ * gensec only handles NULL as no password.
+ */
+ password = NULL;
+ }
+ if (password == NULL) {
+ cli_credentials_set_anonymous(creds);
+ return creds;
+ }
+ }
+
+ tmp = talloc_strdup(creds, username);
+ if (tmp == NULL) {
+ goto fail;
+ }
+ username = tmp;
+
+ /* allow for workgroups as part of the username */
+ if ((p = strchr_m(tmp, '\\')) ||
+ (p = strchr_m(tmp, '/')) ||
+ (p = strchr_m(tmp, *lp_winbind_separator()))) {
+ *p = 0;
+ username = p + 1;
+ domain = tmp;
+ }
+
+ principal = username;
+ username = cli_session_setup_get_account(creds, principal);
+ if (username == NULL) {
+ goto fail;
+ }
+ ok = strequal(username, principal);
+ if (ok) {
+ /*
+ * Ok still the same, so it's not a principal
+ */
+ principal = NULL;
+ }
+
+ if (use_kerberos && fallback_after_kerberos) {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_DESIRED,
+ CRED_SPECIFIED);
+ } else if (use_kerberos) {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+ }
+
+ if (use_ccache) {
+ uint32_t features;
+
+ features = cli_credentials_get_gensec_features(creds);
+ features |= GENSEC_FEATURE_NTLM_CCACHE;
+ cli_credentials_set_gensec_features(creds,
+ features,
+ CRED_SPECIFIED);
+
+ if (password != NULL && strlen(password) == 0) {
+ /*
+ * some callers pass "" as no password
+ *
+ * GENSEC_FEATURE_NTLM_CCACHE only handles
+ * NULL as no password.
+ */
+ password = NULL;
+ }
+ }
+
+ ok = cli_credentials_set_username(creds,
+ username,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+
+ if (domain != NULL) {
+ ok = cli_credentials_set_domain(creds,
+ domain,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (principal != NULL) {
+ ok = cli_credentials_set_principal(creds,
+ principal,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (realm != NULL) {
+ ok = cli_credentials_set_realm(creds,
+ realm,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ if (password != NULL && strlen(password) > 0) {
+ if (password_is_nt_hash) {
+ struct samr_Password nt_hash;
+ size_t converted;
+
+ converted = strhex_to_str((char *)nt_hash.hash,
+ sizeof(nt_hash.hash),
+ password,
+ strlen(password));
+ if (converted != sizeof(nt_hash.hash)) {
+ goto fail;
+ }
+
+ ok = cli_credentials_set_nt_hash(creds,
+ &nt_hash,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ } else {
+ ok = cli_credentials_set_password(creds,
+ password,
+ CRED_SPECIFIED);
+ if (!ok) {
+ goto fail;
+ }
+ }
+ }
+
+ return creds;
+fail:
+ TALLOC_FREE(creds);
+ return NULL;
+}
+
+NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *user_principal = NULL;
+ const char *user_account = NULL;
+ const char *user_domain = NULL;
+ const char *pass = NULL;
+ char *canon_principal = NULL;
+ char *canon_realm = NULL;
+ const char *target_hostname = NULL;
+ enum credentials_use_kerberos krb5_state;
+ bool try_kerberos = false;
+ bool need_kinit = false;
+ bool auth_requested = true;
+ int ret;
+ bool ok;
+
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
+
+ auth_requested = cli_credentials_authentication_requested(creds);
+ if (auth_requested) {
+ errno = 0;
+ user_principal = cli_credentials_get_principal(creds, frame);
+ if (errno != 0) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ user_account = cli_credentials_get_username(creds);
+ user_domain = cli_credentials_get_domain(creds);
+ pass = cli_credentials_get_password(creds);
+
+ krb5_state = cli_credentials_get_kerberos_state(creds);
+
+ if (krb5_state != CRED_USE_KERBEROS_DISABLED) {
+ try_kerberos = true;
+ }
+
+ if (user_principal == NULL) {
+ try_kerberos = false;
+ }
+
+ if (target_hostname == NULL) {
+ try_kerberos = false;
+ } else if (is_ipaddress(target_hostname)) {
+ try_kerberos = false;
+ } else if (strequal(target_hostname, "localhost")) {
+ try_kerberos = false;
+ } else if (strequal(target_hostname, STAR_SMBSERVER)) {
+ try_kerberos = false;
+ } else if (!auth_requested) {
+ try_kerberos = false;
+ }
+
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED && !try_kerberos) {
+ DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access "
+ "'%s' not possible\n",
+ user_principal, user_domain, user_account,
+ target_hostname));
+ TALLOC_FREE(frame);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pass == NULL || strlen(pass) == 0) {
+ need_kinit = false;
+ } else if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ need_kinit = try_kerberos;
+ } else {
+ need_kinit = try_kerberos;
+ }
+
+ if (!need_kinit) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ DBG_INFO("Doing kinit for %s to access %s\n",
+ user_principal, target_hostname);
+
+ /*
+ * TODO: This should be done within the gensec layer
+ * only if required!
+ */
+ setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1);
+ ret = kerberos_kinit_password_ext(user_principal,
+ pass,
+ 0,
+ 0,
+ 0,
+ NULL,
+ false,
+ false,
+ 0,
+ frame,
+ &canon_principal,
+ &canon_realm,
+ NULL);
+ if (ret != 0) {
+ int dbglvl = DBGLVL_NOTICE;
+
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ dbglvl = DBGLVL_ERR;
+ }
+
+ DEBUG(dbglvl, ("Kinit for %s to access %s failed: %s\n",
+ user_principal, target_hostname,
+ error_message(ret)));
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ TALLOC_FREE(frame);
+ return krb5_to_nt_status(ret);
+ }
+
+ /*
+ * Ignore the error and hope that NTLM will work
+ */
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ ok = cli_credentials_set_principal(creds,
+ canon_principal,
+ CRED_SPECIFIED);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ok = cli_credentials_set_realm(creds,
+ canon_realm,
+ CRED_SPECIFIED);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DBG_DEBUG("Successfully authenticated as %s (%s) to access %s using "
+ "Kerberos\n",
+ user_principal,
+ canon_principal,
+ target_hostname);
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli,
+ const char *native_os,
+ const char *native_lm,
+ const char *primary_domain)
+{
+#define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0')
+
+ if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) {
+ cli->server_os = talloc_strdup(cli, native_os);
+ if (cli->server_os == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) {
+ cli->server_type = talloc_strdup(cli, native_lm);
+ if (cli->server_type == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) {
+ cli->server_domain = talloc_strdup(cli, primary_domain);
+ if (cli->server_domain == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+#undef _VALID_STRING
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Utility function to ensure we always return at least
+ a valid char * pointer to an empty string for the
+ cli->server_os, cli->server_type and cli->server_domain
+ strings.
+*******************************************************/
+
+static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx,
+ const uint8_t *hdr,
+ char **dest,
+ uint8_t *src,
+ size_t srclen,
+ ssize_t *destlen)
+{
+ *destlen = pull_string_talloc(mem_ctx,
+ (const char *)hdr,
+ SVAL(hdr, HDR_FLG2),
+ dest,
+ (char *)src,
+ srclen,
+ STR_TERMINATE);
+ if (*destlen == -1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (*dest == NULL) {
+ *dest = talloc_strdup(mem_ctx, "");
+ if (*dest == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Work out suitable capabilities to offer the server.
+****************************************************************************/
+
+static uint32_t cli_session_setup_capabilities(struct cli_state *cli,
+ uint32_t sesssetup_capabilities)
+{
+ uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn);
+
+ /*
+ * We only send capabilities based on the mask for:
+ * - client only flags
+ * - flags used in both directions
+ *
+ * We do not echo the server only flags, except some legacy flags.
+ *
+ * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and
+ * CAP_LARGE_WRITEX in order to allow us to do large reads
+ * against old Samba releases (<= 3.6.x).
+ */
+ client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK);
+
+ /*
+ * Session Setup specific flags CAP_DYNAMIC_REAUTH
+ * and CAP_EXTENDED_SECURITY are passed by the caller.
+ * We need that in order to do guest logins even if
+ * CAP_EXTENDED_SECURITY is negotiated.
+ */
+ client_capabilities &= ~(CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
+ sesssetup_capabilities &= (CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY);
+ client_capabilities |= sesssetup_capabilities;
+
+ return client_capabilities;
+}
+
+/****************************************************************************
+ Do a NT1 guest session setup.
+****************************************************************************/
+
+struct cli_session_setup_guest_state {
+ struct cli_state *cli;
+ uint16_t vwv[13];
+ struct iovec bytes;
+};
+
+static void cli_session_setup_guest_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_session_setup_guest_state *state;
+ uint16_t *vwv;
+ uint8_t *bytes;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_setup_guest_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ vwv = state->vwv;
+
+ SCVAL(vwv+0, 0, 0xFF);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SSVAL(vwv+2, 0, CLI_BUFFER_SIZE);
+ SSVAL(vwv+3, 0, 2);
+ SSVAL(vwv+4, 0, cli_state_get_vc_num(cli));
+ SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn));
+ SSVAL(vwv+7, 0, 0);
+ SSVAL(vwv+8, 0, 0);
+ SSVAL(vwv+9, 0, 0);
+ SSVAL(vwv+10, 0, 0);
+ SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0));
+
+ bytes = talloc_array(state, uint8_t, 0);
+
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* username */
+ NULL);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */
+ NULL);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL);
+
+ if (bytes == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 0, 13,
+ vwv, 1, &state->bytes);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_guest_done, req);
+ *psmbreq = subreq;
+ return req;
+}
+
+struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ NTSTATUS status;
+
+ req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void cli_session_setup_guest_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_setup_guest_state *state = tevent_req_data(
+ req, struct cli_session_setup_guest_state);
+ struct cli_state *cli = state->cli;
+ uint32_t num_bytes;
+ uint8_t *in;
+ uint8_t *inhdr;
+ uint8_t *bytes;
+ uint8_t *p;
+ NTSTATUS status;
+ ssize_t ret;
+ uint8_t wct;
+ uint16_t *vwv;
+
+ status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ inhdr = in + NBT_HDR_SIZE;
+ p = bytes;
+
+ cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID));
+ smb1cli_session_set_action(cli->smb1.session, SVAL(vwv+2, 0));
+
+ status = smb_bytes_talloc_string(cli,
+ inhdr,
+ &cli->server_os,
+ p,
+ bytes+num_bytes-p,
+ &ret);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ p += ret;
+
+ status = smb_bytes_talloc_string(cli,
+ inhdr,
+ &cli->server_type,
+ p,
+ bytes+num_bytes-p,
+ &ret);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ p += ret;
+
+ status = smb_bytes_talloc_string(cli,
+ inhdr,
+ &cli->server_domain,
+ p,
+ bytes+num_bytes-p,
+ &ret);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/* The following is calculated from :
+ * (smb_size-4) = 35
+ * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() )
+ * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at
+ * end of packet.
+ */
+
+#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22)
+
+struct cli_sesssetup_blob_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ DATA_BLOB blob;
+ uint16_t max_blob_size;
+
+ DATA_BLOB this_blob;
+ struct iovec *recv_iov;
+
+ NTSTATUS status;
+ const uint8_t *inbuf;
+ DATA_BLOB ret_blob;
+
+ char *out_native_os;
+ char *out_native_lm;
+};
+
+static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
+ struct tevent_req **psubreq);
+static void cli_sesssetup_blob_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ DATA_BLOB blob)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_sesssetup_blob_state *state;
+ uint32_t usable_space;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_sesssetup_blob_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->blob = blob;
+ state->cli = cli;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ usable_space = UINT16_MAX;
+ } else {
+ usable_space = cli_state_available_size(cli,
+ BASE_SESSSETUP_BLOB_PACKET_SIZE);
+ }
+
+ if (usable_space == 0) {
+ DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small "
+ "(not possible to send %u bytes)\n",
+ BASE_SESSSETUP_BLOB_PACKET_SIZE + 1));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ state->max_blob_size = MIN(usable_space, 0xFFFF);
+
+ if (!cli_sesssetup_blob_next(state, &subreq)) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
+ return req;
+}
+
+static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state,
+ struct tevent_req **psubreq)
+{
+ struct tevent_req *subreq;
+ uint16_t thistime;
+
+ thistime = MIN(state->blob.length, state->max_blob_size);
+
+ state->this_blob.data = state->blob.data;
+ state->this_blob.length = thistime;
+
+ state->blob.data += thistime;
+ state->blob.length -= thistime;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = smb2cli_session_setup_send(state, state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ 0, /* in_flags */
+ SMB2_CAP_DFS, /* in_capabilities */
+ 0, /* in_channel */
+ 0, /* in_previous_session_id */
+ &state->this_blob);
+ if (subreq == NULL) {
+ return false;
+ }
+ } else {
+ uint16_t in_buf_size = 0;
+ uint16_t in_mpx_max = 0;
+ uint16_t in_vc_num = 0;
+ uint32_t in_sess_key = 0;
+ uint32_t in_capabilities = 0;
+ const char *in_native_os = NULL;
+ const char *in_native_lm = NULL;
+
+ in_buf_size = CLI_BUFFER_SIZE;
+ in_mpx_max = smbXcli_conn_max_requests(state->cli->conn);
+ in_vc_num = cli_state_get_vc_num(state->cli);
+ in_sess_key = smb1cli_conn_server_session_key(state->cli->conn);
+ in_capabilities = cli_session_setup_capabilities(state->cli,
+ CAP_EXTENDED_SECURITY);
+ in_native_os = "Unix";
+ in_native_lm = "Samba";
+
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+ in_vc_num = 1;
+ in_sess_key = 0;
+
+ subreq = smb1cli_session_setup_ext_send(state, state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb1.pid,
+ state->cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ state->this_blob,
+ in_capabilities,
+ in_native_os,
+ in_native_lm);
+ if (subreq == NULL) {
+ return false;
+ }
+ }
+ *psubreq = subreq;
+ return true;
+}
+
+static void cli_sesssetup_blob_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_sesssetup_blob_state *state = tevent_req_data(
+ req, struct cli_sesssetup_blob_state);
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_session_setup_recv(subreq, state,
+ &state->recv_iov,
+ &state->ret_blob);
+ } else {
+ status = smb1cli_session_setup_ext_recv(subreq, state,
+ &state->recv_iov,
+ &state->inbuf,
+ &state->ret_blob,
+ &state->out_native_os,
+ &state->out_native_lm);
+ }
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ state->status = status;
+
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->blob.length != 0) {
+ /*
+ * More to send
+ */
+ if (!cli_sesssetup_blob_next(state, &subreq)) {
+ tevent_req_oom(req);
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *pblob,
+ const uint8_t **pinbuf,
+ struct iovec **precv_iov)
+{
+ struct cli_sesssetup_blob_state *state = tevent_req_data(
+ req, struct cli_sesssetup_blob_state);
+ NTSTATUS status;
+ struct iovec *recv_iov;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ TALLOC_FREE(state->cli->smb2.session);
+ cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+ tevent_req_received(req);
+ return status;
+ }
+
+ recv_iov = talloc_move(mem_ctx, &state->recv_iov);
+ if (pblob != NULL) {
+ *pblob = state->ret_blob;
+ }
+ if (pinbuf != NULL) {
+ *pinbuf = state->inbuf;
+ }
+ if (precv_iov != NULL) {
+ *precv_iov = recv_iov;
+ }
+ /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */
+ status = state->status;
+ tevent_req_received(req);
+ return status;
+}
+
+/****************************************************************************
+ Do a spnego/NTLMSSP encrypted session setup.
+****************************************************************************/
+
+struct cli_session_setup_gensec_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct auth_generic_state *auth_generic;
+ bool is_anonymous;
+ DATA_BLOB blob_in;
+ const uint8_t *inbuf;
+ struct iovec *recv_iov;
+ DATA_BLOB blob_out;
+ bool local_ready;
+ bool remote_ready;
+ DATA_BLOB session_key;
+};
+
+static int cli_session_setup_gensec_state_destructor(
+ struct cli_session_setup_gensec_state *state)
+{
+ TALLOC_FREE(state->auth_generic);
+ data_blob_clear_free(&state->session_key);
+ return 0;
+}
+
+static void cli_session_setup_gensec_local_next(struct tevent_req *req);
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req);
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq);
+static void cli_session_setup_gensec_ready(struct tevent_req *req);
+
+static struct tevent_req *cli_session_setup_gensec_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ struct cli_credentials *creds,
+ const char *target_service,
+ const char *target_hostname)
+{
+ struct tevent_req *req;
+ struct cli_session_setup_gensec_state *state;
+ NTSTATUS status;
+ const DATA_BLOB *b = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_setup_gensec_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ talloc_set_destructor(
+ state, cli_session_setup_gensec_state_destructor);
+
+ status = auth_generic_client_prepare(state, &state->auth_generic);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = auth_generic_set_creds(state->auth_generic, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ gensec_want_feature(state->auth_generic->gensec_security,
+ GENSEC_FEATURE_SESSION_KEY);
+
+ if (target_service != NULL) {
+ status = gensec_set_target_service(
+ state->auth_generic->gensec_security,
+ target_service);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (target_hostname != NULL) {
+ status = gensec_set_target_hostname(
+ state->auth_generic->gensec_security,
+ target_hostname);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ b = smbXcli_conn_server_gss_blob(cli->conn);
+ if (b != NULL) {
+ state->blob_in = *b;
+ }
+
+ state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials);
+
+ status = auth_generic_client_start(state->auth_generic,
+ GENSEC_OID_SPNEGO);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ state->cli->smb2.session = smbXcli_session_create(cli,
+ cli->conn);
+ if (tevent_req_nomem(state->cli->smb2.session, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ cli_session_setup_gensec_local_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void cli_session_setup_gensec_local_next(struct tevent_req *req)
+{
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ struct tevent_req *subreq = NULL;
+
+ if (state->local_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = gensec_update_send(state, state->ev,
+ state->auth_generic->gensec_security,
+ state->blob_in);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req);
+}
+
+static void cli_session_setup_gensec_local_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ NTSTATUS status;
+
+ status = gensec_update_recv(subreq, state, &state->blob_out);
+ TALLOC_FREE(subreq);
+ state->blob_in = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ state->local_ready = true;
+ }
+
+ if (state->local_ready && state->remote_ready) {
+ cli_session_setup_gensec_ready(req);
+ return;
+ }
+
+ cli_session_setup_gensec_remote_next(req);
+}
+
+static void cli_session_setup_gensec_remote_next(struct tevent_req *req)
+{
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ struct tevent_req *subreq = NULL;
+
+ if (state->remote_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = cli_sesssetup_blob_send(state, state->ev,
+ state->cli, state->blob_out);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_session_setup_gensec_remote_done,
+ req);
+}
+
+static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ NTSTATUS status;
+
+ state->inbuf = NULL;
+ TALLOC_FREE(state->recv_iov);
+
+ status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in,
+ &state->inbuf, &state->recv_iov);
+ TALLOC_FREE(subreq);
+ data_blob_free(&state->blob_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct smbXcli_session *session = NULL;
+ bool is_guest = false;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ session = state->cli->smb2.session;
+ } else {
+ session = state->cli->smb1.session;
+ }
+
+ is_guest = smbXcli_session_is_guest(session);
+ if (is_guest) {
+ /*
+ * We can't finish the gensec handshake, we don't
+ * have a negotiated session key.
+ *
+ * So just pretend we are completely done,
+ * we need to continue as anonymous from this point,
+ * as we can't get a session key.
+ *
+ * Note that smbXcli_session_is_guest()
+ * always returns false if we require signing.
+ */
+ state->blob_in = data_blob_null;
+ state->local_ready = true;
+ state->is_anonymous = true;
+ }
+
+ state->remote_ready = true;
+ }
+
+ if (state->local_ready && state->remote_ready) {
+ cli_session_setup_gensec_ready(req);
+ return;
+ }
+
+ cli_session_setup_gensec_local_next(req);
+}
+
+static void cli_session_dump_keys(TALLOC_CTX *mem_ctx,
+ struct smbXcli_session *session,
+ DATA_BLOB session_key)
+{
+ NTSTATUS status;
+ DATA_BLOB sig = data_blob_null;
+ DATA_BLOB app = data_blob_null;
+ DATA_BLOB enc = data_blob_null;
+ DATA_BLOB dec = data_blob_null;
+ uint64_t sid = smb2cli_session_current_id(session);
+
+ status = smb2cli_session_signing_key(session, mem_ctx, &sig);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smbXcli_session_application_key(session, mem_ctx, &app);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smb2cli_session_encryption_key(session, mem_ctx, &enc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ status = smb2cli_session_decryption_key(session, mem_ctx, &dec);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ DEBUG(0, ("debug encryption: dumping generated session keys\n"));
+ DEBUGADD(0, ("Session Id "));
+ dump_data(0, (uint8_t*)&sid, sizeof(sid));
+ DEBUGADD(0, ("Session Key "));
+ dump_data(0, session_key.data, session_key.length);
+ DEBUGADD(0, ("Signing Key "));
+ dump_data(0, sig.data, sig.length);
+ DEBUGADD(0, ("App Key "));
+ dump_data(0, app.data, app.length);
+
+ /* In client code, ServerIn is the encryption key */
+
+ DEBUGADD(0, ("ServerIn Key "));
+ dump_data(0, enc.data, enc.length);
+ DEBUGADD(0, ("ServerOut Key "));
+ dump_data(0, dec.data, dec.length);
+
+out:
+ data_blob_clear_free(&sig);
+ data_blob_clear_free(&app);
+ data_blob_clear_free(&enc);
+ data_blob_clear_free(&dec);
+}
+
+static void cli_session_setup_gensec_ready(struct tevent_req *req)
+{
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ const char *server_domain = NULL;
+ NTSTATUS status;
+
+ if (state->blob_in.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->blob_out.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /*
+ * gensec_ntlmssp_server_domain() returns NULL
+ * if NTLMSSP is not used.
+ *
+ * We can remove this later
+ * and leave the server domain empty for SMB2 and above
+ * in future releases.
+ */
+ server_domain = gensec_ntlmssp_server_domain(
+ state->auth_generic->gensec_security);
+
+ if (state->cli->server_domain[0] == '\0' && server_domain != NULL) {
+ TALLOC_FREE(state->cli->server_domain);
+ state->cli->server_domain = talloc_strdup(state->cli,
+ server_domain);
+ if (state->cli->server_domain == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ if (state->is_anonymous) {
+ /*
+ * Windows server does not set the
+ * SMB2_SESSION_FLAG_IS_NULL flag.
+ *
+ * This fix makes sure we do not try
+ * to verify a signature on the final
+ * session setup response.
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ status = gensec_session_key(state->auth_generic->gensec_security,
+ state, &state->session_key);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ struct smbXcli_session *session = state->cli->smb2.session;
+
+ status = smb2cli_session_set_session_key(session,
+ state->session_key,
+ state->recv_iov);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB3_00
+ && lp_debug_encryption())
+ {
+ cli_session_dump_keys(state, session, state->session_key);
+ }
+ } else {
+ struct smbXcli_session *session = state->cli->smb1.session;
+ bool active;
+
+ status = smb1cli_session_set_session_key(session,
+ state->session_key);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ active = smb1cli_conn_activate_signing(state->cli->conn,
+ state->session_key,
+ data_blob_null);
+ if (active) {
+ bool ok;
+
+ ok = smb1cli_conn_check_signing(state->cli->conn,
+ state->inbuf, 1);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req)
+{
+ struct cli_session_setup_gensec_state *state =
+ tevent_req_data(req,
+ struct cli_session_setup_gensec_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx,
+ const char *principal)
+{
+ char *account, *p;
+
+ account = talloc_strdup(mem_ctx, principal);
+ if (account == NULL) {
+ return NULL;
+ }
+ p = strchr_m(account, '@');
+ if (p != NULL) {
+ *p = '\0';
+ }
+ return account;
+}
+
+/****************************************************************************
+ Do a spnego encrypted session setup.
+
+ user_domain: The shortname of the domain the user/machine is a member of.
+ dest_realm: The realm we're connecting to, if NULL we use our default realm.
+****************************************************************************/
+
+struct cli_session_setup_spnego_state {
+ ADS_STATUS result;
+};
+
+static void cli_session_setup_spnego_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_session_setup_spnego_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_session_setup_spnego_state *state;
+ const char *target_service = NULL;
+ const char *target_hostname = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_setup_spnego_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ target_service = "cifs";
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
+
+ status = cli_session_creds_prepare_krb5(cli, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ DBG_INFO("Connect to %s as %s using SPNEGO\n",
+ target_hostname,
+ cli_credentials_get_principal(creds, talloc_tos()));
+
+ subreq = cli_session_setup_gensec_send(state, ev, cli, creds,
+ target_service, target_hostname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_session_setup_spnego_done, req);
+ return req;
+}
+
+static void cli_session_setup_spnego_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_session_setup_gensec_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req)
+{
+ struct cli_session_setup_spnego_state *state = tevent_req_data(
+ req, struct cli_session_setup_spnego_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ state->result = ADS_ERROR_NT(status);
+ }
+
+ return state->result;
+}
+
+struct cli_session_setup_creds_state {
+ struct cli_state *cli;
+ DATA_BLOB apassword_blob;
+ DATA_BLOB upassword_blob;
+ DATA_BLOB lm_session_key;
+ DATA_BLOB session_key;
+ char *out_native_os;
+ char *out_native_lm;
+ char *out_primary_domain;
+};
+
+static void cli_session_setup_creds_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
+
+ if (req_state != TEVENT_REQ_RECEIVED) {
+ return;
+ }
+
+ /*
+ * We only call data_blob_clear() as
+ * some of the blobs point to the same memory.
+ *
+ * We let the talloc hierarchy free the memory.
+ */
+ data_blob_clear(&state->apassword_blob);
+ data_blob_clear(&state->upassword_blob);
+ data_blob_clear(&state->lm_session_key);
+ data_blob_clear(&state->session_key);
+ ZERO_STRUCTP(state);
+}
+
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq);
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq);
+
+/****************************************************************************
+ Send a session setup. The username and workgroup is in UNIX character
+ format and must be converted to DOS codepage format before sending. If the
+ password is in plaintext, the same should be done.
+****************************************************************************/
+
+struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_session_setup_creds_state *state;
+ uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+ bool use_spnego = false;
+ int flags = 0;
+ const char *username = "";
+ const char *domain = "";
+ DATA_BLOB target_info = data_blob_null;
+ DATA_BLOB challenge = data_blob_null;
+ uint16_t in_buf_size = 0;
+ uint16_t in_mpx_max = 0;
+ uint16_t in_vc_num = 0;
+ uint32_t in_sess_key = 0;
+ const char *in_native_os = NULL;
+ const char *in_native_lm = NULL;
+ enum credentials_use_kerberos krb5_state =
+ cli_credentials_get_kerberos_state(creds);
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_setup_creds_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup);
+
+ /*
+ * Now work out what sort of session setup we are going to
+ * do. I have split this into separate functions to make the flow a bit
+ * easier to understand (tridge).
+ */
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) {
+ use_spnego = false;
+ } else if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ use_spnego = true;
+ } else if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) {
+ /*
+ * if the server supports extended security then use SPNEGO
+ * even for anonymous connections.
+ */
+ use_spnego = true;
+ } else {
+ use_spnego = false;
+ }
+
+ if (use_spnego) {
+ subreq = cli_session_setup_spnego_send(
+ state, ev, cli, creds);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_spnego,
+ req);
+ return req;
+ }
+
+ if (krb5_state == CRED_USE_KERBEROS_REQUIRED) {
+ DBG_WARNING("Kerberos authentication requested, but "
+ "the server does not support SPNEGO authentication\n");
+ tevent_req_nterror(req, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT);
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) {
+ /*
+ * SessionSetupAndX was introduced by LANMAN 1.0. So we skip
+ * this step against older servers.
+ */
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (cli_credentials_is_anonymous(creds)) {
+ /*
+ * Do an anonymous session setup
+ */
+ goto non_spnego_creds_done;
+ }
+
+ if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) {
+ /*
+ * Do an anonymous session setup,
+ * the password is passed via the tree connect.
+ */
+ goto non_spnego_creds_done;
+ }
+
+ cli_credentials_get_ntlm_username_domain(creds, state,
+ &username,
+ &domain);
+ if (tevent_req_nomem(username, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (tevent_req_nomem(domain, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ DBG_INFO("Connect to %s as %s using NTLM\n", domain, username);
+
+ if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) {
+ bool use_unicode = smbXcli_conn_use_unicode(cli->conn);
+ uint8_t *bytes = NULL;
+ size_t bytes_len = 0;
+ const char *pw = cli_credentials_get_password(creds);
+ size_t pw_len = 0;
+
+ if (pw == NULL) {
+ pw = "";
+ }
+ pw_len = strlen(pw) + 1;
+
+ if (!lp_client_plaintext_auth()) {
+ DEBUG(1, ("Server requested PLAINTEXT password but "
+ "'client plaintext auth = no'\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
+
+ bytes = talloc_array(state, uint8_t, 0);
+ bytes = trans2_bytes_push_str(bytes, use_unicode,
+ pw, pw_len, &bytes_len);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (use_unicode) {
+ /*
+ * CAP_UNICODE, can only be negotiated by NT1.
+ */
+ state->upassword_blob = data_blob_const(bytes,
+ bytes_len);
+ } else {
+ state->apassword_blob = data_blob_const(bytes,
+ bytes_len);
+ }
+
+ goto non_spnego_creds_done;
+ }
+
+ challenge = data_blob_const(smb1cli_conn_server_challenge(cli->conn), 8);
+
+ if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
+ if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) {
+ /*
+ * Don't send an NTLMv2 response without NTLMSSP if we
+ * want to use spnego support.
+ */
+ DEBUG(1, ("Server does not support EXTENDED_SECURITY "
+ " but 'client use spnego = yes'"
+ " and 'client ntlmv2 auth = yes' is set\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
+
+ if (lp_client_ntlmv2_auth()) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+
+ /*
+ * note that the 'domain' here is a best
+ * guess - we don't know the server's domain
+ * at this point. Windows clients also don't
+ * use hostname...
+ */
+ target_info = NTLMv2_generate_names_blob(state,
+ NULL,
+ domain);
+ if (tevent_req_nomem(target_info.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ flags |= CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth()) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+ }
+ } else {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested user level LM password but "
+ "'client lanman auth = no' is set.\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
+
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ status = cli_credentials_get_ntlm_response(creds, state, &flags,
+ challenge, NULL,
+ target_info,
+ &state->apassword_blob,
+ &state->upassword_blob,
+ &state->lm_session_key,
+ &state->session_key);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+non_spnego_creds_done:
+
+ in_buf_size = CLI_BUFFER_SIZE;
+ in_mpx_max = smbXcli_conn_max_requests(cli->conn);
+ in_vc_num = cli_state_get_vc_num(cli);
+ in_sess_key = smb1cli_conn_server_session_key(cli->conn);
+ in_native_os = "Unix";
+ in_native_lm = "Samba";
+
+ if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) {
+ uint32_t in_capabilities = 0;
+
+ in_capabilities = cli_session_setup_capabilities(cli, 0);
+
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+
+ subreq = smb1cli_session_setup_nt1_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ username,
+ domain,
+ state->apassword_blob,
+ state->upassword_blob,
+ in_capabilities,
+ in_native_os,
+ in_native_lm);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_nt1,
+ req);
+ return req;
+ }
+
+ /*
+ * For now we keep the same values as before,
+ * we may remove these in a separate commit later.
+ */
+ in_mpx_max = 2;
+ in_vc_num = 1;
+
+ subreq = smb1cli_session_setup_lm21_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.session,
+ in_buf_size,
+ in_mpx_max,
+ in_vc_num,
+ in_sess_key,
+ username,
+ domain,
+ state->apassword_blob,
+ in_native_os,
+ in_native_lm);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_setup_creds_done_lm21,
+ req);
+ return req;
+}
+
+static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ ADS_STATUS status;
+
+ status = cli_session_setup_spnego_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
+ tevent_req_nterror(req, ads_ntstatus(status));
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
+ struct cli_state *cli = state->cli;
+ NTSTATUS status;
+ struct iovec *recv_iov = NULL;
+ const uint8_t *inbuf = NULL;
+ bool ok;
+
+ status = smb1cli_session_setup_nt1_recv(subreq, state,
+ &recv_iov,
+ &inbuf,
+ &state->out_native_os,
+ &state->out_native_lm,
+ &state->out_primary_domain);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status)));
+ return;
+ }
+
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ state->out_primary_domain);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ok = smb1cli_conn_activate_signing(cli->conn,
+ state->session_key,
+ state->upassword_blob);
+ if (ok) {
+ ok = smb1cli_conn_check_signing(cli->conn, inbuf, 1);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+ }
+
+ if (state->session_key.data) {
+ struct smbXcli_session *session = cli->smb1.session;
+
+ status = smb1cli_session_set_session_key(session,
+ state->session_key);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_setup_creds_state *state = tevent_req_data(
+ req, struct cli_session_setup_creds_state);
+ NTSTATUS status;
+
+ status = smb1cli_session_setup_lm21_recv(subreq, state,
+ &state->out_native_os,
+ &state->out_native_lm);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status)));
+ return;
+ }
+
+ status = cli_state_update_after_sesssetup(state->cli,
+ state->out_native_os,
+ state->out_native_lm,
+ NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_session_setup_creds(struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_session_setup_creds_send(ev, ev, cli, creds);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_session_setup_creds_recv(req);
+ fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+NTSTATUS cli_session_setup_anon(struct cli_state *cli)
+{
+ NTSTATUS status;
+ struct cli_credentials *creds = NULL;
+
+ creds = cli_credentials_init_anon(cli);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_session_setup_creds(cli, creds);
+ TALLOC_FREE(creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send a uloggoff.
+*****************************************************************************/
+
+struct cli_ulogoff_state {
+ struct cli_state *cli;
+ uint16_t vwv[3];
+};
+
+static void cli_ulogoff_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_ulogoff_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ SCVAL(state->vwv+0, 0, 0xFF);
+ SCVAL(state->vwv+1, 0, 0);
+ SSVAL(state->vwv+2, 0, 0);
+
+ subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 0, 2, state->vwv,
+ 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ulogoff_done, req);
+ return req;
+}
+
+static void cli_ulogoff_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_ulogoff_state *state = tevent_req_data(
+ req, struct cli_ulogoff_state);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ cli_state_set_uid(state->cli, UID_FIELD_INVALID);
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_ulogoff_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_ulogoff(struct cli_state *cli)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_logoff(cli->conn,
+ cli->timeout,
+ cli->smb2.session);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ smb2cli_session_set_id_and_flags(cli->smb2.session,
+ UINT64_MAX, 0);
+ return NT_STATUS_OK;
+ }
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_ulogoff_send(ev, ev, cli);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_ulogoff_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/****************************************************************************
+ Send a tconX.
+****************************************************************************/
+
+struct cli_tcon_andx_state {
+ struct cli_state *cli;
+ uint16_t vwv[4];
+ struct iovec bytes;
+};
+
+static void cli_tcon_andx_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *share, const char *dev,
+ const char *pass, int passlen,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_tcon_andx_state *state;
+ uint8_t p24[24];
+ uint16_t *vwv;
+ char *tmp = NULL;
+ uint8_t *bytes;
+ uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn);
+ uint16_t tcon_flags = 0;
+
+ *psmbreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ vwv = state->vwv;
+
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
+ cli->share = talloc_strdup(cli, share);
+ if (!cli->share) {
+ return NULL;
+ }
+
+ /* in user level security don't send a password now */
+ if (sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) {
+ passlen = 1;
+ pass = "";
+ } else if (pass == NULL) {
+ DEBUG(1, ("Server not using user level security and no "
+ "password supplied.\n"));
+ goto access_denied;
+ }
+
+ if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
+ *pass && passlen != 24) {
+ if (!lp_client_lanman_auth()) {
+ DEBUG(1, ("Server requested LANMAN password "
+ "(share-level security) but "
+ "'client lanman auth = no' or 'client ntlmv2 auth = yes'\n"));
+ goto access_denied;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage before
+ * encryption.
+ */
+ SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24);
+ passlen = 24;
+ pass = (const char *)p24;
+ } else {
+ if((sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL
+ |NEGOTIATE_SECURITY_CHALLENGE_RESPONSE))
+ == 0) {
+ uint8_t *tmp_pass;
+
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested PLAINTEXT "
+ "password but "
+ "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n"));
+ goto access_denied;
+ }
+
+ /*
+ * Non-encrypted passwords - convert to DOS codepage
+ * before using.
+ */
+ tmp_pass = talloc_array(talloc_tos(), uint8_t, 0);
+ if (tevent_req_nomem(tmp_pass, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tmp_pass = trans2_bytes_push_str(tmp_pass,
+ false, /* always DOS */
+ pass,
+ passlen,
+ NULL);
+ if (tevent_req_nomem(tmp_pass, req)) {
+ return tevent_req_post(req, ev);
+ }
+ pass = (const char *)tmp_pass;
+ passlen = talloc_get_size(tmp_pass);
+ }
+ }
+
+ tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+
+ SCVAL(vwv+0, 0, 0xFF);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SSVAL(vwv+2, 0, tcon_flags);
+ SSVAL(vwv+3, 0, passlen);
+
+ if (passlen && pass) {
+ bytes = (uint8_t *)talloc_memdup(state, pass, passlen);
+ } else {
+ bytes = talloc_array(state, uint8_t, 0);
+ }
+
+ /*
+ * Add the sharename
+ */
+ tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn), share);
+ if (tmp == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1,
+ NULL);
+ TALLOC_FREE(tmp);
+
+ /*
+ * Add the devicetype
+ */
+ tmp = talloc_strdup_upper(talloc_tos(), dev);
+ if (tmp == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL);
+ TALLOC_FREE(tmp);
+
+ if (bytes == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 0, 4, vwv,
+ 1, &state->bytes);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cli_tcon_andx_done, req);
+ *psmbreq = subreq;
+ return req;
+
+ access_denied:
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+}
+
+struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *share, const char *dev,
+ const char *pass, int passlen)
+{
+ struct tevent_req *req, *subreq;
+ NTSTATUS status;
+
+ req = cli_tcon_andx_create(mem_ctx, ev, cli, share, dev, pass, passlen,
+ &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+ if (subreq == NULL) {
+ return req;
+ }
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void cli_tcon_andx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_tcon_andx_state *state = tevent_req_data(
+ req, struct cli_tcon_andx_state);
+ struct cli_state *cli = state->cli;
+ uint8_t *in;
+ uint8_t *inhdr;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+ uint16_t optional_support = 0;
+
+ status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ inhdr = in + NBT_HDR_SIZE;
+
+ if (num_bytes) {
+ if (pull_string_talloc(cli,
+ (const char *)inhdr,
+ SVAL(inhdr, HDR_FLG2),
+ &cli->dev,
+ bytes,
+ num_bytes,
+ STR_TERMINATE|STR_ASCII) == -1) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ } else {
+ cli->dev = talloc_strdup(cli, "");
+ if (cli->dev == NULL) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ }
+
+ if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 3)) {
+ /* almost certainly win95 - enable bug fixes */
+ cli->win95 = True;
+ }
+
+ /*
+ * Make sure that we have the optional support 16-bit field. WCT > 2.
+ * Avoids issues when connecting to Win9x boxes sharing files
+ */
+
+ if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) {
+ optional_support = SVAL(vwv+2, 0);
+ }
+
+ if (optional_support & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(cli->smb1.session);
+ }
+
+ smb1cli_tcon_set_values(state->cli->smb1.tcon,
+ SVAL(inhdr, HDR_TID),
+ optional_support,
+ 0, /* maximal_access */
+ 0, /* guest_maximal_access */
+ NULL, /* service */
+ NULL); /* fs_type */
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_tcon_andx_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass, int passlen)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_tcon_andx_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_tree_connect_state {
+ struct cli_state *cli;
+};
+
+static struct tevent_req *cli_raw_tcon_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *service, const char *pass, const char *dev);
+static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
+ uint16_t *max_xmit, uint16_t *tid);
+
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq);
+static void cli_tree_connect_andx_done(struct tevent_req *subreq);
+static void cli_tree_connect_raw_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_tree_connect_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *share, const char *dev, const char *pass)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_tree_connect_state *state;
+ int passlen;
+
+ if (pass == NULL) {
+ pass = "";
+ }
+ passlen = strlen(pass) + 1;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_tree_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ cli->share = talloc_strdup(cli, share);
+ if (tevent_req_nomem(cli->share, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ char *unc;
+
+ TALLOC_FREE(cli->smb2.tcon);
+ cli->smb2.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb2.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ unc = talloc_asprintf(state, "\\\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ share);
+ if (tevent_req_nomem(unc, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout,
+ cli->smb2.session, cli->smb2.tcon,
+ 0, /* flags */
+ unc);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tree_connect_smb2_done,
+ req);
+ return req;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) {
+ subreq = cli_tcon_andx_send(state, ev, cli, share, dev,
+ pass, passlen);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tree_connect_andx_done,
+ req);
+ return req;
+ }
+
+ subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req);
+
+ return req;
+}
+
+static void cli_tree_connect_smb2_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = smb2cli_tcon_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_tree_connect_andx_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_tcon_andx_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_tree_connect_raw_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_tree_connect_state *state = tevent_req_data(
+ req, struct cli_tree_connect_state);
+ NTSTATUS status;
+ uint16_t max_xmit = 0;
+ uint16_t tid = 0;
+
+ status = cli_raw_tcon_recv(subreq, &max_xmit, &tid);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ smb1cli_tcon_set_values(state->cli->smb1.tcon,
+ tid,
+ 0, /* optional_support */
+ 0, /* maximal_access */
+ 0, /* guest_maximal_access */
+ NULL, /* service */
+ NULL); /* fs_type */
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_tree_connect_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_tree_connect_send(ev, ev, cli, share, dev, pass);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_tree_connect_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
+ const char *share, const char *dev,
+ struct cli_credentials *creds)
+{
+ const char *pw = NULL;
+
+ if (creds != NULL) {
+ pw = cli_credentials_get_password(creds);
+ }
+
+ return cli_tree_connect(cli, share, dev, pw);
+}
+
+/****************************************************************************
+ Send a tree disconnect.
+****************************************************************************/
+
+struct cli_tdis_state {
+ struct cli_state *cli;
+};
+
+static void cli_tdis_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_tdis_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, 0, NULL, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_tdis_done, req);
+ return req;
+}
+
+static void cli_tdis_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_tdis_state *state = tevent_req_data(
+ req, struct cli_tdis_state);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ TALLOC_FREE(state->cli->smb1.tcon);
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_tdis_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_tdis(struct cli_state *cli)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_tdis(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon);
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(cli->smb2.tcon);
+ }
+ return status;
+ }
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_tdis_send(ev, ev, cli);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_tdis_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+struct cli_connect_sock_state {
+ const char **called_names;
+ const char **calling_names;
+ int *called_types;
+ int fd;
+ uint16_t port;
+};
+
+static void cli_connect_sock_done(struct tevent_req *subreq);
+
+/*
+ * Async only if we don't have to look up the name, i.e. "pss" is set with a
+ * nonzero address.
+ */
+
+static struct tevent_req *cli_connect_sock_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *host, int name_type, const struct sockaddr_storage *pss,
+ const char *myname, uint16_t port)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_connect_sock_state *state;
+ struct sockaddr_storage *addrs = NULL;
+ unsigned i;
+ unsigned num_addrs = 0;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_connect_sock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if ((pss == NULL) || is_zero_addr(pss)) {
+
+ /*
+ * Here we cheat. resolve_name_list is not async at all. So
+ * this call will only be really async if the name lookup has
+ * been done externally.
+ */
+
+ status = resolve_name_list(state, host, name_type,
+ &addrs, &num_addrs);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ addrs = talloc_array(state, struct sockaddr_storage, 1);
+ if (tevent_req_nomem(addrs, req)) {
+ return tevent_req_post(req, ev);
+ }
+ addrs[0] = *pss;
+ num_addrs = 1;
+ }
+
+ state->called_names = talloc_array(state, const char *, num_addrs);
+ if (tevent_req_nomem(state->called_names, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->called_types = talloc_array(state, int, num_addrs);
+ if (tevent_req_nomem(state->called_types, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->calling_names = talloc_array(state, const char *, num_addrs);
+ if (tevent_req_nomem(state->calling_names, req)) {
+ return tevent_req_post(req, ev);
+ }
+ for (i=0; i<num_addrs; i++) {
+ state->called_names[i] = host;
+ state->called_types[i] = name_type;
+ state->calling_names[i] = myname;
+ }
+
+ subreq = smbsock_any_connect_send(
+ state, ev, addrs, state->called_names, state->called_types,
+ state->calling_names, NULL, num_addrs, port);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_connect_sock_done, req);
+ return req;
+}
+
+static void cli_connect_sock_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_connect_sock_state *state = tevent_req_data(
+ req, struct cli_connect_sock_state);
+ NTSTATUS status;
+
+ status = smbsock_any_connect_recv(subreq, &state->fd, NULL,
+ &state->port);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ set_socket_options(state->fd, lp_socket_options());
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_connect_sock_recv(struct tevent_req *req,
+ int *pfd, uint16_t *pport)
+{
+ struct cli_connect_sock_state *state = tevent_req_data(
+ req, struct cli_connect_sock_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfd = state->fd;
+ *pport = state->port;
+ return NT_STATUS_OK;
+}
+
+struct cli_connect_nb_state {
+ const char *desthost;
+ enum smb_signing_setting signing_state;
+ int flags;
+ struct cli_state *cli;
+};
+
+static void cli_connect_nb_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_connect_nb_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *host, const struct sockaddr_storage *dest_ss,
+ uint16_t port, int name_type, const char *myname,
+ enum smb_signing_setting signing_state, int flags)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_connect_nb_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->signing_state = signing_state;
+ state->flags = flags;
+
+ if (host != NULL) {
+ char *p = strchr(host, '#');
+
+ if (p != NULL) {
+ name_type = strtol(p+1, NULL, 16);
+ host = talloc_strndup(state, host, p - host);
+ if (tevent_req_nomem(host, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ state->desthost = host;
+ } else if (dest_ss != NULL) {
+ state->desthost = print_canonical_sockaddr(state, dest_ss);
+ if (tevent_req_nomem(state->desthost, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ /* No host or dest_ss given. Error out. */
+ tevent_req_error(req, EINVAL);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss,
+ myname, port);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_connect_nb_done, req);
+ return req;
+}
+
+static void cli_connect_nb_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_connect_nb_state *state = tevent_req_data(
+ req, struct cli_connect_nb_state);
+ NTSTATUS status;
+ int fd = 0;
+ uint16_t port;
+
+ status = cli_connect_sock_recv(subreq, &fd, &port);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->cli = cli_state_create(state, fd, state->desthost,
+ state->signing_state, state->flags);
+ if (tevent_req_nomem(state->cli, req)) {
+ close(fd);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_connect_nb_recv(struct tevent_req *req,
+ struct cli_state **pcli)
+{
+ struct cli_connect_nb_state *state = tevent_req_data(
+ req, struct cli_connect_nb_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pcli = talloc_move(NULL, &state->cli);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
+ uint16_t port, int name_type, const char *myname,
+ enum smb_signing_setting signing_state, int flags, struct cli_state **pcli)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_connect_nb_send(ev, ev, host, dest_ss, port, name_type,
+ myname, signing_state, flags);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_connect_nb_recv(req, pcli);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+struct cli_start_connection_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ int min_protocol;
+ int max_protocol;
+ struct smb2_negotiate_contexts *negotiate_contexts;
+};
+
+static void cli_start_connection_connected(struct tevent_req *subreq);
+static void cli_start_connection_done(struct tevent_req *subreq);
+
+/**
+ establishes a connection to after the negprot.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ss (optional) The destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+*/
+
+static struct tevent_req *cli_start_connection_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *my_name, const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ enum smb_signing_setting signing_state, int flags,
+ struct smb2_negotiate_contexts *negotiate_contexts)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_start_connection_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_start_connection_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ if (flags & CLI_FULL_CONNECTION_IPC) {
+ state->min_protocol = lp_client_ipc_min_protocol();
+ state->max_protocol = lp_client_ipc_max_protocol();
+ } else {
+ state->min_protocol = lp_client_min_protocol();
+ state->max_protocol = lp_client_max_protocol();
+ }
+
+ if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) {
+ state->max_protocol = MIN(state->max_protocol,
+ PROTOCOL_NT1);
+ state->min_protocol = MIN(state->min_protocol,
+ state->max_protocol);
+ }
+
+ if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) {
+ state->min_protocol = MAX(state->min_protocol,
+ PROTOCOL_SMB2_02);
+ state->max_protocol = MAX(state->max_protocol,
+ state->min_protocol);
+ }
+
+ state->negotiate_contexts = talloc_zero(
+ state, struct smb2_negotiate_contexts);
+ if (tevent_req_nomem(state->negotiate_contexts, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (flags & CLI_FULL_CONNECTION_REQUEST_POSIX) {
+ NTSTATUS status;
+
+ status = smb2_negotiate_context_add(
+ state->negotiate_contexts,
+ state->negotiate_contexts,
+ SMB2_POSIX_EXTENSIONS_AVAILABLE,
+ (const uint8_t *)SMB2_CREATE_TAG_POSIX,
+ strlen(SMB2_CREATE_TAG_POSIX));
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (negotiate_contexts != NULL) {
+ uint16_t i;
+
+ for (i=0; i<negotiate_contexts->num_contexts; i++) {
+ struct smb2_negotiate_context *ctx =
+ &negotiate_contexts->contexts[i];
+ NTSTATUS status;
+
+ status = smb2_negotiate_context_add(
+ state->negotiate_contexts,
+ state->negotiate_contexts,
+ ctx->type,
+ ctx->data.data,
+ ctx->data.length);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+ }
+
+ subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port,
+ 0x20, my_name, signing_state, flags);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_start_connection_connected, req);
+ return req;
+}
+
+static void cli_start_connection_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
+ NTSTATUS status;
+
+ status = cli_connect_nb_recv(subreq, &state->cli);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = smbXcli_negprot_send(
+ state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->min_protocol,
+ state->max_protocol,
+ WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK,
+ state->negotiate_contexts);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_start_connection_done, req);
+}
+
+static void cli_start_connection_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
+ NTSTATUS status;
+
+ status = smbXcli_negprot_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ /* Ensure we ask for some initial credits. */
+ smb2cli_conn_set_max_credits(state->cli->conn,
+ DEFAULT_SMB2_MAX_CREDITS);
+ }
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
+ struct cli_state **output_cli)
+{
+ struct cli_start_connection_state *state = tevent_req_data(
+ req, struct cli_start_connection_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *output_cli = state->cli;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ enum smb_signing_setting signing_state, int flags)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss,
+ port, signing_state, flags, NULL);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_start_connection_recv(req, output_cli);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+struct cli_smb1_setup_encryption_blob_state {
+ uint16_t setup[1];
+ uint8_t param[4];
+ NTSTATUS status;
+ DATA_BLOB out;
+ uint16_t enc_ctx_id;
+};
+
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_setup_encryption_blob_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const DATA_BLOB in)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb1_setup_encryption_blob_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb1_setup_encryption_blob_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (in.length > CLI_BUFFER_SIZE) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO);
+ SSVAL(state->param, 0, 0);
+ SSVAL(state->param, 2, SMB_REQUEST_TRANSPORT_ENCRYPTION);
+
+ subreq = smb1cli_trans_send(state, ev, cli->conn,
+ SMBtrans2,
+ 0, 0, /* _flags */
+ 0, 0, /* _flags2 */
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ state->setup, 1, 0,
+ state->param, 4, 2,
+ in.data, in.length, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb1_setup_encryption_blob_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_blob_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_blob_state);
+ uint8_t *rparam=NULL, *rdata=NULL;
+ uint32_t num_rparam, num_rdata;
+ NTSTATUS status;
+
+ status = smb1cli_trans_recv(subreq, state,
+ NULL, /* recv_flags */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 0, &num_rparam,
+ &rdata, 0, &num_rdata);
+ TALLOC_FREE(subreq);
+ state->status = status;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ status = NT_STATUS_OK;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (num_rparam == 2) {
+ state->enc_ctx_id = SVAL(rparam, 0);
+ }
+ TALLOC_FREE(rparam);
+
+ state->out = data_blob_const(rdata, num_rdata);
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb1_setup_encryption_blob_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *out,
+ uint16_t *enc_ctx_id)
+{
+ struct cli_smb1_setup_encryption_blob_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_blob_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ status = state->status;
+
+ *out = state->out;
+ talloc_steal(mem_ctx, out->data);
+
+ *enc_ctx_id = state->enc_ctx_id;
+
+ tevent_req_received(req);
+ return status;
+}
+
+struct cli_smb1_setup_encryption_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb_trans_enc_state *es;
+ DATA_BLOB blob_in;
+ DATA_BLOB blob_out;
+ bool local_ready;
+ bool remote_ready;
+};
+
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req);
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq);
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req);
+
+static struct tevent_req *cli_smb1_setup_encryption_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_req *req = NULL;
+ struct cli_smb1_setup_encryption_state *state = NULL;
+ struct auth_generic_state *ags = NULL;
+ const DATA_BLOB *b = NULL;
+ bool auth_requested = false;
+ const char *target_service = NULL;
+ const char *target_hostname = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb1_setup_encryption_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ auth_requested = cli_credentials_authentication_requested(creds);
+ if (!auth_requested) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
+ return tevent_req_post(req, ev);
+ }
+
+ target_service = "cifs";
+ target_hostname = smbXcli_conn_remote_name(cli->conn);
+
+ status = cli_session_creds_prepare_krb5(cli, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->es = talloc_zero(state, struct smb_trans_enc_state);
+ if (tevent_req_nomem(state->es, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ status = auth_generic_client_prepare(state->es, &ags);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ gensec_want_feature(ags->gensec_security,
+ GENSEC_FEATURE_SIGN);
+ gensec_want_feature(ags->gensec_security,
+ GENSEC_FEATURE_SEAL);
+
+ status = auth_generic_set_creds(ags, creds);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (target_service != NULL) {
+ status = gensec_set_target_service(ags->gensec_security,
+ target_service);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ if (target_hostname != NULL) {
+ status = gensec_set_target_hostname(ags->gensec_security,
+ target_hostname);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ gensec_set_max_update_size(ags->gensec_security,
+ CLI_BUFFER_SIZE);
+
+ b = smbXcli_conn_server_gss_blob(state->cli->conn);
+ if (b != NULL) {
+ state->blob_in = *b;
+ }
+
+ status = auth_generic_client_start(ags, GENSEC_OID_SPNEGO);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * We only need the gensec_security part from here.
+ */
+ state->es->gensec_security = talloc_move(state->es,
+ &ags->gensec_security);
+ TALLOC_FREE(ags);
+
+ cli_smb1_setup_encryption_local_next(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void cli_smb1_setup_encryption_local_next(struct tevent_req *req)
+{
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct tevent_req *subreq = NULL;
+
+ if (state->local_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = gensec_update_send(state, state->ev,
+ state->es->gensec_security,
+ state->blob_in);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req);
+}
+
+static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ NTSTATUS status;
+
+ status = gensec_update_recv(subreq, state, &state->blob_out);
+ TALLOC_FREE(subreq);
+ state->blob_in = data_blob_null;
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ state->local_ready = true;
+ }
+
+ /*
+ * We always get NT_STATUS_OK from the server even if it is not ready.
+ * So guess the server is ready when we are ready and already sent
+ * our last blob to the server.
+ */
+ if (state->local_ready && state->blob_out.length == 0) {
+ state->remote_ready = true;
+ }
+
+ if (state->local_ready && state->remote_ready) {
+ cli_smb1_setup_encryption_ready(req);
+ return;
+ }
+
+ cli_smb1_setup_encryption_remote_next(req);
+}
+
+static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req)
+{
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct tevent_req *subreq = NULL;
+
+ if (state->remote_ready) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ subreq = cli_smb1_setup_encryption_blob_send(state, state->ev,
+ state->cli, state->blob_out);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb1_setup_encryption_remote_done,
+ req);
+}
+
+static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ NTSTATUS status;
+
+ status = cli_smb1_setup_encryption_blob_recv(subreq, state,
+ &state->blob_in,
+ &state->es->enc_ctx_num);
+ TALLOC_FREE(subreq);
+ data_blob_free(&state->blob_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+ {
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ /*
+ * We always get NT_STATUS_OK even if the server is not ready.
+ * So guess the server is ready when we are ready and sent
+ * our last blob to the server.
+ */
+ if (state->local_ready) {
+ state->remote_ready = true;
+ }
+
+ if (state->local_ready && state->remote_ready) {
+ cli_smb1_setup_encryption_ready(req);
+ return;
+ }
+
+ cli_smb1_setup_encryption_local_next(req);
+}
+
+static void cli_smb1_setup_encryption_ready(struct tevent_req *req)
+{
+ struct cli_smb1_setup_encryption_state *state =
+ tevent_req_data(req,
+ struct cli_smb1_setup_encryption_state);
+ struct smb_trans_enc_state *es = NULL;
+
+ if (state->blob_in.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (state->blob_out.length != 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ es = talloc_move(state->cli->conn, &state->es);
+ es->enc_on = true;
+ smb1cli_conn_set_encryption(state->cli->conn, es);
+ es = NULL;
+
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
+ struct cli_credentials *creds)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb1_setup_encryption_send(ev, ev, cli, creds);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb1_setup_encryption_recv(req);
+ fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/**
+ establishes a connection right up to doing tconX, password specified.
+ @param output_cli A fully initialised cli structure, non-null only on success
+ @param dest_host The netbios name of the remote host
+ @param dest_ip (optional) The the destination IP, NULL for name based lookup
+ @param port (optional) The destination port (0 for default)
+ @param service (optional) The share to make the connection to. Should be 'unqualified' in any way.
+ @param service_type The 'type' of service.
+ @param creds The used user credentials
+*/
+
+struct cli_full_connection_creds_state {
+ struct tevent_context *ev;
+ const char *service;
+ const char *service_type;
+ struct cli_credentials *creds;
+ int flags;
+ struct cli_state *cli;
+};
+
+static int cli_full_connection_creds_state_destructor(
+ struct cli_full_connection_creds_state *s)
+{
+ if (s->cli != NULL) {
+ cli_shutdown(s->cli);
+ s->cli = NULL;
+ }
+ return 0;
+}
+
+static void cli_full_connection_creds_conn_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_sess_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_start(struct tevent_req *req);
+static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_done(struct tevent_req *subreq);
+static void cli_full_connection_creds_enc_tdis(struct tevent_req *req);
+static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq);
+static void cli_full_connection_creds_tcon_start(struct tevent_req *req);
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_full_connection_creds_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *my_name, const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ struct cli_credentials *creds,
+ int flags,
+ struct smb2_negotiate_contexts *negotiate_contexts)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_full_connection_creds_state *state;
+ enum smb_signing_setting signing_state;
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(creds);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_full_connection_creds_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(state, cli_full_connection_creds_state_destructor);
+
+ state->ev = ev;
+ state->service = service;
+ state->service_type = service_type;
+ state->creds = creds;
+ state->flags = flags;
+
+ if (flags & CLI_FULL_CONNECTION_IPC) {
+ signing_state = cli_credentials_get_smb_ipc_signing(creds);
+ } else {
+ signing_state = cli_credentials_get_smb_signing(creds);
+ }
+
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED) {
+ if (flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK) {
+ encryption_state = SMB_ENCRYPTION_DESIRED;
+ }
+ }
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ signing_state = SMB_SIGNING_REQUIRED;
+ }
+
+ subreq = cli_start_connection_send(
+ state, ev, my_name, dest_host, dest_ss, port,
+ signing_state, flags,
+ negotiate_contexts);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_conn_done,
+ req);
+ return req;
+}
+
+static void cli_full_connection_creds_conn_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ NTSTATUS status;
+
+ status = cli_start_connection_recv(subreq, &state->cli);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_session_setup_creds_send(
+ state, state->ev, state->cli, state->creds);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_sess_done,
+ req);
+}
+
+static void cli_full_connection_creds_sess_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ NTSTATUS status;
+
+ status = cli_session_setup_creds_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ (state->flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) {
+
+ state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+ state->creds = cli_credentials_init_anon(state);
+ if (tevent_req_nomem(state->creds, req)) {
+ return;
+ }
+
+ subreq = cli_session_setup_creds_send(
+ state, state->ev, state->cli, state->creds);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_sess_done,
+ req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ cli_full_connection_creds_enc_start(req);
+}
+
+static void cli_full_connection_creds_enc_start(struct tevent_req *req)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(state->creds);
+ struct tevent_req *subreq = NULL;
+ NTSTATUS status;
+
+ if (encryption_state < SMB_ENCRYPTION_DESIRED) {
+ cli_full_connection_creds_tcon_start(req);
+ return;
+ }
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_session_encryption_on(state->cli->smb2.session);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ cli_full_connection_creds_tcon_start(req);
+ return;
+ }
+ d_printf("Encryption required and "
+ "server doesn't support "
+ "SMB3 encryption - failing connect\n");
+ tevent_req_nterror(req, status);
+ return;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(status));
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ cli_full_connection_creds_tcon_start(req);
+ return;
+ }
+
+ if (!SERVER_HAS_UNIX_CIFS(state->cli)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ cli_full_connection_creds_tcon_start(req);
+ return;
+ }
+
+ status = NT_STATUS_NOT_SUPPORTED;
+ d_printf("Encryption required and "
+ "server doesn't support "
+ "SMB1 Unix Extensions - failing connect\n");
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ /*
+ * We do a tcon on IPC$ just to setup the encryption,
+ * the real tcon will be encrypted then.
+ */
+ subreq = cli_tree_connect_send(state, state->ev, state->cli,
+ "IPC$", "IPC", NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_full_connection_creds_enc_tcon, req);
+}
+
+static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ NTSTATUS status;
+
+ status = cli_tree_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_unix_extensions_version_send(state, state->ev, state->cli);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_full_connection_creds_enc_ver, req);
+}
+
+static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(state->creds);
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ NTSTATUS status;
+
+ status = cli_unix_extensions_version_recv(subreq,
+ &major, &minor,
+ &caplow,
+ &caphigh);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ /* disconnect ipc$ followed by the real tree connect */
+ cli_full_connection_creds_enc_tdis(req);
+ return;
+ }
+ DEBUG(10, ("%s: cli_unix_extensions_version "
+ "returned %s\n", __func__, nt_errstr(status)));
+ tevent_req_nterror(req, NT_STATUS_UNKNOWN_REVISION);
+ return;
+ }
+
+ if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
+ if (encryption_state < SMB_ENCRYPTION_REQUIRED) {
+ /* disconnect ipc$ followed by the real tree connect */
+ cli_full_connection_creds_enc_tdis(req);
+ return;
+ }
+ DEBUG(10, ("%s: CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP "
+ "not supported\n", __func__));
+ tevent_req_nterror(req, NT_STATUS_UNSUPPORTED_COMPRESSION);
+ return;
+ }
+
+ subreq = cli_smb1_setup_encryption_send(state, state->ev,
+ state->cli,
+ state->creds);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_enc_done,
+ req);
+}
+
+static void cli_full_connection_creds_enc_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb1_setup_encryption_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /* disconnect ipc$ followed by the real tree connect */
+ cli_full_connection_creds_enc_tdis(req);
+}
+
+static void cli_full_connection_creds_enc_tdis(struct tevent_req *req)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ struct tevent_req *subreq = NULL;
+
+ subreq = cli_tdis_send(state, state->ev, state->cli);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_enc_finished,
+ req);
+}
+
+static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_tdis_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ cli_full_connection_creds_tcon_start(req);
+}
+
+static void cli_full_connection_creds_tcon_start(struct tevent_req *req)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ struct tevent_req *subreq = NULL;
+ const char *password = NULL;
+
+ if (state->service == NULL) {
+ tevent_req_done(req);
+ return;
+ }
+
+ password = cli_credentials_get_password(state->creds);
+
+ subreq = cli_tree_connect_send(state, state->ev,
+ state->cli,
+ state->service,
+ state->service_type,
+ password);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_full_connection_creds_tcon_done,
+ req);
+}
+
+static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_tree_connect_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
+ struct cli_state **output_cli)
+{
+ struct cli_full_connection_creds_state *state = tevent_req_data(
+ req, struct cli_full_connection_creds_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *output_cli = state->cli;
+ talloc_set_destructor(state, NULL);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_full_connection_creds(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ struct cli_credentials *creds,
+ int flags)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_full_connection_creds_send(
+ ev, ev, my_name, dest_host, dest_ss, port, service,
+ service_type, creds, flags,
+ NULL);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_full_connection_creds_recv(req, output_cli);
+ fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/****************************************************************************
+ Send an old style tcon.
+****************************************************************************/
+struct cli_raw_tcon_state {
+ uint16_t *ret_vwv;
+};
+
+static void cli_raw_tcon_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_raw_tcon_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ const char *service, const char *pass, const char *dev)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_raw_tcon_state *state;
+ uint8_t *bytes;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_raw_tcon_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (!lp_client_plaintext_auth() && (*pass)) {
+ DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'"
+ " or 'client ntlmv2 auth = yes'\n"));
+ tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+ return tevent_req_post(req, ev);
+ }
+
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = smbXcli_tcon_create(cli);
+ if (tevent_req_nomem(cli->smb1.tcon, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX);
+
+ bytes = talloc_array(state, uint8_t, 0);
+ bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
+ service, strlen(service)+1, NULL);
+ bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
+ pass, strlen(pass)+1, NULL);
+ bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0);
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn),
+ dev, strlen(dev)+1, NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, 0, NULL,
+ talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_raw_tcon_done, req);
+ return req;
+}
+
+static void cli_raw_tcon_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_raw_tcon_state *state = tevent_req_data(
+ req, struct cli_raw_tcon_state);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 2, NULL, &state->ret_vwv,
+ NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req,
+ uint16_t *max_xmit, uint16_t *tid)
+{
+ struct cli_raw_tcon_state *state = tevent_req_data(
+ req, struct cli_raw_tcon_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *max_xmit = SVAL(state->ret_vwv + 0, 0);
+ *tid = SVAL(state->ret_vwv + 1, 0);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+ const char *service, const char *pass, const char *dev,
+ uint16_t *max_xmit, uint16_t *tid)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_raw_tcon_send(ev, ev, cli, service, pass, dev);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_raw_tcon_recv(req, max_xmit, tid);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+/* Return a cli_state pointing at the IPC$ share for the given server */
+
+struct cli_state *get_ipc_connect(char *server,
+ struct sockaddr_storage *server_ss,
+ struct cli_credentials *creds)
+{
+ struct cli_state *cli;
+ NTSTATUS nt_status;
+ uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+ flags |= CLI_FULL_CONNECTION_FORCE_SMB1;
+ flags |= CLI_FULL_CONNECTION_IPC;
+
+ nt_status = cli_full_connection_creds(&cli, NULL, server, server_ss, 0, "IPC$", "IPC",
+ creds,
+ flags);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return cli;
+ }
+ if (is_ipaddress(server)) {
+ /* windows 9* needs a correct NMB name for connections */
+ fstring remote_name;
+
+ if (name_status_find("*", 0, 0, server_ss, remote_name)) {
+ cli = get_ipc_connect(remote_name, server_ss, creds);
+ if (cli)
+ return cli;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * 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(TALLOC_CTX *ctx,
+ struct sockaddr_storage *mb_ip,
+ struct cli_credentials *creds,
+ char **pp_workgroup_out)
+{
+ char addr[INET6_ADDRSTRLEN];
+ fstring name;
+ struct cli_state *cli;
+ struct sockaddr_storage server_ss;
+
+ *pp_workgroup_out = NULL;
+
+ print_sockaddr(addr, sizeof(addr), mb_ip);
+ DEBUG(99, ("Looking up name of master browser %s\n",
+ addr));
+
+ /*
+ * 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, name) &&
+ !name_status_find(MSBROWSE, 1, 0x1d, mb_ip, name)) {
+
+ DEBUG(99, ("Could not retrieve name status for %s\n",
+ addr));
+ return NULL;
+ }
+
+ if (!find_master_ip(name, &server_ss)) {
+ DEBUG(99, ("Could not find master ip for %s\n", name));
+ return NULL;
+ }
+
+ *pp_workgroup_out = talloc_strdup(ctx, name);
+
+ DEBUG(4, ("found master browser %s, %s\n", name, addr));
+
+ print_sockaddr(addr, sizeof(addr), &server_ss);
+ cli = get_ipc_connect(addr, &server_ss, creds);
+
+ return cli;
+}
diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c
new file mode 100644
index 0000000..939b3b0
--- /dev/null
+++ b/source3/libsmb/clidfs.c
@@ -0,0 +1,1451 @@
+/*
+ Unix SMB/CIFS implementation.
+ client connect/disconnect routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) Jeremy Allison 2007-2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "msdfs.h"
+#include "trans2.h"
+#include "libsmb/nmblib.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "auth/credentials/credentials.h"
+#include "lib/param/param.h"
+#include "libcli/smb/smb2_negotiate_context.h"
+
+/********************************************************************
+ Important point.
+
+ DFS paths are *always* of the form \server\share\<pathname> (the \ characters
+ are not C escaped here).
+
+ - but if we're using POSIX paths then <pathname> may contain
+ '/' separators, not '\\' separators. So cope with '\\' or '/'
+ as a separator when looking at the pathname part.... JRA.
+********************************************************************/
+
+/********************************************************************
+ Ensure a connection is encrypted.
+********************************************************************/
+
+static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c,
+ struct cli_credentials *creds,
+ const char *sharename)
+{
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ NTSTATUS status;
+ bool temp_ipc = false;
+
+ if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) {
+ status = smb2cli_session_encryption_on(c->smb2.session);
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
+ d_printf("Encryption required and "
+ "server doesn't support "
+ "SMB3 encryption - failing connect\n");
+ } else if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(status));
+ }
+ return status;
+ }
+
+ if (!SERVER_HAS_UNIX_CIFS(c)) {
+ d_printf("Encryption required and "
+ "server that doesn't support "
+ "UNIX extensions - failing connect\n");
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (c->smb1.tcon == NULL) {
+ status = cli_tree_connect_creds(c, "IPC$", "IPC", creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "can't connect to IPC$ to check "
+ "UNIX CIFS extensions.\n");
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+ temp_ipc = true;
+ }
+
+ status = cli_unix_extensions_version(c, &major, &minor, &caplow,
+ &caphigh);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "can't get UNIX CIFS extensions "
+ "version from server.\n");
+ if (temp_ipc) {
+ cli_tdis(c);
+ }
+ return NT_STATUS_UNKNOWN_REVISION;
+ }
+
+ if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) {
+ d_printf("Encryption required and "
+ "share %s doesn't support "
+ "encryption.\n", sharename);
+ if (temp_ipc) {
+ cli_tdis(c);
+ }
+ return NT_STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ status = cli_smb1_setup_encryption(c, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(status));
+ if (temp_ipc) {
+ cli_tdis(c);
+ }
+ return status;
+ }
+
+ if (temp_ipc) {
+ cli_tdis(c);
+ }
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Return a connection to a server.
+********************************************************************/
+
+static NTSTATUS do_connect(TALLOC_CTX *ctx,
+ const char *server,
+ const char *share,
+ struct cli_credentials *creds,
+ const struct sockaddr_storage *dest_ss,
+ int port,
+ int name_type,
+ struct cli_state **pcli)
+{
+ struct cli_state *c = NULL;
+ char *servicename;
+ char *sharename;
+ char *newserver, *newshare;
+ NTSTATUS status;
+ int flags = 0;
+ enum protocol_types protocol = PROTOCOL_NONE;
+ enum smb_signing_setting signing_state =
+ cli_credentials_get_smb_signing(creds);
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(creds);
+ struct smb2_negotiate_contexts *in_contexts = NULL;
+ struct smb2_negotiate_contexts *out_contexts = NULL;
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ signing_state = SMB_SIGNING_REQUIRED;
+ }
+
+ /* make a copy so we don't modify the global string 'service' */
+ servicename = talloc_strdup(ctx,share);
+ if (!servicename) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sharename = servicename;
+ if (*sharename == '\\') {
+ sharename += 2;
+ if (server == NULL) {
+ server = sharename;
+ }
+ sharename = strchr_m(sharename,'\\');
+ if (!sharename) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *sharename = 0;
+ sharename++;
+ }
+ if (server == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = cli_connect_nb(
+ server, dest_ss, port, name_type, NULL,
+ signing_state,
+ flags, &c);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ DBG_ERR("NetBIOS support disabled, unable to connect\n");
+ }
+
+ DBG_WARNING("Connection to %s failed (Error %s)\n",
+ server,
+ nt_errstr(status));
+ return status;
+ }
+
+ DEBUG(4,(" session request ok\n"));
+
+ in_contexts = talloc_zero(ctx, struct smb2_negotiate_contexts);
+ if (in_contexts == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = smb2_negotiate_context_add(
+ in_contexts,
+ in_contexts,
+ SMB2_POSIX_EXTENSIONS_AVAILABLE,
+ (const uint8_t *)SMB2_CREATE_TAG_POSIX,
+ strlen(SMB2_CREATE_TAG_POSIX));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smbXcli_negprot(c->conn,
+ c->timeout,
+ lp_client_min_protocol(),
+ lp_client_max_protocol(),
+ in_contexts,
+ ctx,
+ &out_contexts);
+ TALLOC_FREE(in_contexts);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ d_printf("Protocol negotiation (with timeout %d ms) timed out against server %s\n",
+ c->timeout,
+ smbXcli_conn_remote_name(c->conn));
+ cli_shutdown(c);
+ return status;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Protocol negotiation to server %s (for a protocol between %s and %s) failed: %s\n",
+ smbXcli_conn_remote_name(c->conn),
+ lpcfg_get_smb_protocol(lp_client_min_protocol()),
+ lpcfg_get_smb_protocol(lp_client_max_protocol()),
+ nt_errstr(status));
+ cli_shutdown(c);
+ return status;
+ }
+ protocol = smbXcli_conn_protocol(c->conn);
+ DEBUG(4,(" negotiated dialect[%s] against server[%s]\n",
+ smb_protocol_types_string(protocol),
+ smbXcli_conn_remote_name(c->conn)));
+
+ if (protocol >= PROTOCOL_SMB2_02) {
+ /* Ensure we ask for some initial credits. */
+ smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS);
+ }
+
+ status = cli_session_setup_creds(c, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* If a password was not supplied then
+ * try again with a null username. */
+ if (encryption_state == SMB_ENCRYPTION_REQUIRED ||
+ smbXcli_conn_signing_mandatory(c->conn) ||
+ cli_credentials_authentication_requested(creds) ||
+ cli_credentials_is_anonymous(creds) ||
+ !NT_STATUS_IS_OK(status = cli_session_setup_anon(c)))
+ {
+ d_printf("session setup failed: %s\n",
+ nt_errstr(status));
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED))
+ d_printf("did you forget to run kinit?\n");
+ cli_shutdown(c);
+ return status;
+ }
+ d_printf("Anonymous login successful\n");
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status)));
+ cli_shutdown(c);
+ return status;
+ }
+
+ DEBUG(4,(" session setup ok\n"));
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ status = cli_cm_force_encryption_creds(c,
+ creds,
+ sharename);
+ if (!NT_STATUS_IS_OK(status)) {
+ switch (encryption_state) {
+ case SMB_ENCRYPTION_DESIRED:
+ break;
+ case SMB_ENCRYPTION_REQUIRED:
+ default:
+ cli_shutdown(c);
+ return status;
+ }
+ }
+ }
+
+ /* 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, sharename,
+ &newserver, &newshare,
+ creds)) {
+ cli_shutdown(c);
+ return do_connect(ctx, newserver,
+ newshare, creds,
+ NULL, port, name_type, pcli);
+ }
+
+ /* must be a normal share */
+
+ status = cli_tree_connect_creds(c, sharename, "?????", creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("tree connect failed: %s\n", nt_errstr(status));
+ cli_shutdown(c);
+ return status;
+ }
+
+ DEBUG(4,(" tconx ok\n"));
+ *pcli = c;
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Add a new connection to the list.
+ referring_cli == NULL means a new initial connection.
+********************************************************************/
+
+static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx,
+ struct cli_state *referring_cli,
+ const char *server,
+ const char *share,
+ struct cli_credentials *creds,
+ const struct sockaddr_storage *dest_ss,
+ int port,
+ int name_type,
+ struct cli_state **pcli)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+
+ status = do_connect(ctx, server, share,
+ creds,
+ dest_ss, port, name_type, &cli);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * This can't happen, this test is to satisfy static
+ * checkers (clang)
+ */
+ if (cli == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Enter into the list. */
+ if (referring_cli) {
+ DLIST_ADD_END(referring_cli, cli);
+ }
+
+ if (referring_cli && referring_cli->requested_posix_capabilities) {
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ status = cli_unix_extensions_version(cli, &major, &minor,
+ &caplow, &caphigh);
+ if (NT_STATUS_IS_OK(status)) {
+ cli_set_unix_extensions_capabilities(cli,
+ major, minor,
+ caplow, caphigh);
+ }
+ }
+
+ *pcli = cli;
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Return a connection to a server on a particular share.
+********************************************************************/
+
+static struct cli_state *cli_cm_find(struct cli_state *cli,
+ const char *server,
+ const char *share)
+{
+ struct cli_state *p;
+
+ if (cli == NULL) {
+ return NULL;
+ }
+
+ /* Search to the start of the list. */
+ for (p = cli; p; p = DLIST_PREV(p)) {
+ const char *remote_name =
+ smbXcli_conn_remote_name(p->conn);
+
+ if (strequal(server, remote_name) &&
+ strequal(share,p->share)) {
+ return p;
+ }
+ }
+
+ /* Search to the end of the list. */
+ for (p = cli->next; p; p = p->next) {
+ const char *remote_name =
+ smbXcli_conn_remote_name(p->conn);
+
+ if (strequal(server, remote_name) &&
+ strequal(share,p->share)) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+/****************************************************************************
+ Open a client connection to a \\server\share.
+****************************************************************************/
+
+NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
+ struct cli_state *referring_cli,
+ const char *server,
+ const char *share,
+ struct cli_credentials *creds,
+ const struct sockaddr_storage *dest_ss,
+ int port,
+ int name_type,
+ struct cli_state **pcli)
+{
+ /* Try to reuse an existing connection in this list. */
+ struct cli_state *c = cli_cm_find(referring_cli, server, share);
+ NTSTATUS status;
+
+ if (c) {
+ *pcli = c;
+ return NT_STATUS_OK;
+ }
+
+ if (creds == NULL) {
+ /* Can't do a new connection
+ * without auth info. */
+ d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] "
+ "without client credentials\n",
+ server, share );
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = cli_cm_connect(ctx,
+ referring_cli,
+ server,
+ share,
+ creds,
+ dest_ss,
+ port,
+ name_type,
+ &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pcli = c;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_display(struct cli_state *cli)
+{
+ int i;
+
+ for (i=0; cli; cli = cli->next,i++ ) {
+ d_printf("%d:\tserver=%s, share=%s\n",
+ i, smbXcli_conn_remote_name(cli->conn), cli->share);
+ }
+}
+
+/**********************************************************************
+ split a dfs path into the server, share name, and extrapath components
+**********************************************************************/
+
+static bool split_dfs_path(TALLOC_CTX *ctx,
+ const char *nodepath,
+ char **pp_server,
+ char **pp_share,
+ char **pp_extrapath)
+{
+ char *p, *q;
+ char *path;
+
+ *pp_server = NULL;
+ *pp_share = NULL;
+ *pp_extrapath = NULL;
+
+ path = talloc_strdup(ctx, nodepath);
+ if (!path) {
+ goto fail;
+ }
+
+ if ( path[0] != '\\' ) {
+ goto fail;
+ }
+
+ p = strchr_m( path + 1, '\\' );
+ if ( !p ) {
+ goto fail;
+ }
+
+ *p = '\0';
+ p++;
+
+ /* Look for any extra/deep path */
+ q = strchr_m(p, '\\');
+ if (q != NULL) {
+ *q = '\0';
+ q++;
+ *pp_extrapath = talloc_strdup(ctx, q);
+ } else {
+ *pp_extrapath = talloc_strdup(ctx, "");
+ }
+ if (*pp_extrapath == NULL) {
+ goto fail;
+ }
+
+ *pp_share = talloc_strdup(ctx, p);
+ if (*pp_share == NULL) {
+ goto fail;
+ }
+
+ *pp_server = talloc_strdup(ctx, &path[1]);
+ if (*pp_server == NULL) {
+ goto fail;
+ }
+
+ TALLOC_FREE(path);
+ return true;
+
+fail:
+ TALLOC_FREE(*pp_share);
+ TALLOC_FREE(*pp_extrapath);
+ TALLOC_FREE(path);
+ return false;
+}
+
+/****************************************************************************
+ Return the original path truncated at the directory component before
+ the first wildcard character. Trust the caller to provide a NULL
+ terminated string
+****************************************************************************/
+
+static char *clean_path(TALLOC_CTX *ctx, const char *path)
+{
+ size_t len;
+ char *p1, *p2, *p;
+ char *path_out;
+
+ /* No absolute paths. */
+ while (IS_DIRECTORY_SEP(*path)) {
+ path++;
+ }
+
+ path_out = talloc_strdup(ctx, path);
+ if (!path_out) {
+ return NULL;
+ }
+
+ p1 = strchr_m(path_out, '*');
+ p2 = strchr_m(path_out, '?');
+
+ if (p1 || p2) {
+ if (p1 && p2) {
+ p = MIN(p1,p2);
+ } else if (!p1) {
+ p = p2;
+ } else {
+ p = p1;
+ }
+ *p = '\0';
+
+ /* Now go back to the start of this component. */
+ p1 = strrchr_m(path_out, '/');
+ p2 = strrchr_m(path_out, '\\');
+ p = MAX(p1,p2);
+ if (p) {
+ *p = '\0';
+ }
+ }
+
+ /* Strip any trailing separator */
+
+ len = strlen(path_out);
+ if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) {
+ path_out[len-1] = '\0';
+ }
+
+ return path_out;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+static char *cli_dfs_make_full_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *dir)
+{
+ char path_sep = '\\';
+
+ /* Ensure the extrapath doesn't start with a separator. */
+ while (IS_DIRECTORY_SEP(*dir)) {
+ dir++;
+ }
+
+ if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ path_sep = '/';
+ }
+ return talloc_asprintf(ctx, "%c%s%c%s%c%s",
+ path_sep,
+ smbXcli_conn_remote_name(cli->conn),
+ path_sep,
+ cli->share,
+ path_sep,
+ dir);
+}
+
+/********************************************************************
+ Check if a path has already been converted to DFS.
+********************************************************************/
+
+bool cli_dfs_is_already_full_path(struct cli_state *cli, const char *path)
+{
+ const char *server = smbXcli_conn_remote_name(cli->conn);
+ size_t server_len = strlen(server);
+ bool found_server = false;
+ const char *share = cli->share;
+ size_t share_len = strlen(share);
+ bool found_share = false;
+
+ if (!IS_DIRECTORY_SEP(path[0])) {
+ return false;
+ }
+ path++;
+ found_server = (strncasecmp_m(path, server, server_len) == 0);
+ if (!found_server) {
+ return false;
+ }
+ path += server_len;
+ if (!IS_DIRECTORY_SEP(path[0])) {
+ return false;
+ }
+ path++;
+ found_share = (strncasecmp_m(path, share, share_len) == 0);
+ if (!found_share) {
+ return false;
+ }
+ path += share_len;
+ if (path[0] == '\0') {
+ return true;
+ }
+ if (IS_DIRECTORY_SEP(path[0])) {
+ return true;
+ }
+ return false;
+}
+
+/********************************************************************
+ Get the dfs referral link.
+********************************************************************/
+
+NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path,
+ uint16_t max_referral_level,
+ struct client_dfs_referral **refs,
+ size_t *num_refs,
+ size_t *consumed)
+{
+ unsigned int param_len = 0;
+ uint16_t recv_flags2;
+ uint8_t *param = NULL;
+ uint8_t *rdata = NULL;
+ char *p;
+ char *endp;
+ smb_ucs2_t *path_ucs;
+ char *consumed_path = NULL;
+ uint16_t consumed_ucs;
+ uint16_t num_referrals;
+ struct client_dfs_referral *referrals = NULL;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ *num_refs = 0;
+ *refs = NULL;
+
+ param = talloc_array(talloc_tos(), uint8_t, 2);
+ if (!param) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ SSVAL(param, 0, max_referral_level);
+
+ param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn),
+ path, strlen(path)+1,
+ NULL);
+ if (!param) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ param_len = talloc_get_size(param);
+ path_ucs = (smb_ucs2_t *)&param[2];
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ DATA_BLOB in_input_buffer;
+ DATA_BLOB in_output_buffer = data_blob_null;
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+
+ in_input_buffer.data = param;
+ in_input_buffer.length = param_len;
+
+ status = smb2cli_ioctl(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ UINT64_MAX, /* in_fid_persistent */
+ UINT64_MAX, /* in_fid_volatile */
+ FSCTL_DFS_GET_REFERRALS,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ CLI_BUFFER_SIZE, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL,
+ talloc_tos(),
+ &out_input_buffer,
+ &out_output_buffer);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ if (out_output_buffer.length < 4) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto out;
+ }
+
+ recv_flags2 = FLAGS2_UNICODE_STRINGS;
+ rdata = out_output_buffer.data;
+ endp = (char *)rdata + out_output_buffer.length;
+ } else {
+ unsigned int data_len = 0;
+ uint16_t setup[1];
+
+ SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, 0xffff, 0, 0,
+ setup, 1, 0,
+ param, param_len, 2,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &recv_flags2,
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL,
+ &rdata, 4, &data_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ endp = (char *)rdata + data_len;
+ }
+
+ consumed_ucs = SVAL(rdata, 0);
+ num_referrals = SVAL(rdata, 2);
+
+ /* consumed_ucs is the number of bytes
+ * of the UCS2 path consumed not counting any
+ * terminating null. We need to convert
+ * back to unix charset and count again
+ * to get the number of bytes consumed from
+ * the incoming path. */
+
+ errno = 0;
+ if (pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ &consumed_path,
+ path_ucs,
+ consumed_ucs,
+ STR_UNICODE) == 0) {
+ if (errno != 0) {
+ status = map_nt_error_from_unix(errno);
+ } else {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ goto out;
+ }
+ if (consumed_path == NULL) {
+ status = map_nt_error_from_unix(errno);
+ goto out;
+ }
+ *consumed = strlen(consumed_path);
+
+ if (num_referrals != 0) {
+ uint16_t ref_version;
+ uint16_t ref_size;
+ int i;
+ uint16_t node_offset;
+
+ referrals = talloc_array(ctx, struct client_dfs_referral,
+ num_referrals);
+
+ if (!referrals) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ /* start at the referrals array */
+
+ p = (char *)rdata+8;
+ for (i=0; i<num_referrals && p < endp; i++) {
+ if (p + 18 > endp) {
+ goto out;
+ }
+ ref_version = SVAL(p, 0);
+ ref_size = SVAL(p, 2);
+ node_offset = SVAL(p, 16);
+
+ if (ref_version != 3) {
+ p += ref_size;
+ continue;
+ }
+
+ referrals[i].proximity = SVAL(p, 8);
+ referrals[i].ttl = SVAL(p, 10);
+
+ if (p + node_offset > endp) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto out;
+ }
+ pull_string_talloc(referrals,
+ (const char *)rdata,
+ recv_flags2,
+ &referrals[i].dfspath,
+ p+node_offset,
+ PTR_DIFF(endp, p+node_offset),
+ STR_TERMINATE|STR_UNICODE);
+
+ if (!referrals[i].dfspath) {
+ status = map_nt_error_from_unix(errno);
+ goto out;
+ }
+ p += ref_size;
+ }
+ if (i < num_referrals) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto out;
+ }
+ }
+
+ *num_refs = num_referrals;
+ *refs = referrals;
+
+ out:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path,
+ struct client_dfs_referral **refs,
+ size_t *num_refs,
+ size_t *consumed)
+{
+ return cli_dfs_get_referral_ex(ctx,
+ cli,
+ path,
+ 3,
+ refs, /* Max referral level we want */
+ num_refs,
+ consumed);
+}
+
+static bool cli_conn_have_dfs(struct cli_state *cli)
+{
+ struct smbXcli_conn *conn = cli->conn;
+ struct smbXcli_tcon *tcon = NULL;
+ bool ok;
+
+ if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) {
+ uint32_t capabilities = smb1cli_conn_capabilities(conn);
+
+ if ((capabilities & CAP_STATUS32) == 0) {
+ return false;
+ }
+ if ((capabilities & CAP_UNICODE) == 0) {
+ return false;
+ }
+
+ tcon = cli->smb1.tcon;
+ } else {
+ tcon = cli->smb2.tcon;
+ }
+
+ ok = smbXcli_tcon_is_dfs_share(tcon);
+ return ok;
+}
+
+/********************************************************************
+********************************************************************/
+struct cli_dfs_path_split {
+ char *server;
+ char *share;
+ char *extrapath;
+};
+
+NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
+ const char *mountpt,
+ struct cli_credentials *creds,
+ struct cli_state *rootcli,
+ const char *path,
+ struct cli_state **targetcli,
+ char **pp_targetpath)
+{
+ struct client_dfs_referral *refs = NULL;
+ size_t num_refs = 0;
+ size_t consumed = 0;
+ struct cli_state *cli_ipc = NULL;
+ char *dfs_path = NULL;
+ char *cleanpath = NULL;
+ char *extrapath = NULL;
+ int pathlen;
+ struct cli_state *newcli = NULL;
+ struct cli_state *ccli = NULL;
+ size_t count = 0;
+ char *newpath = NULL;
+ char *newmount = NULL;
+ char *ppath = NULL;
+ SMB_STRUCT_STAT sbuf;
+ uint32_t attributes;
+ NTSTATUS status;
+ struct smbXcli_tcon *target_tcon = NULL;
+ struct cli_dfs_path_split *dfs_refs = NULL;
+ bool ok;
+ bool is_already_dfs = false;
+
+ if ( !rootcli || !path || !targetcli ) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Avoid more than one leading directory separator
+ */
+ while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) {
+ path++;
+ }
+
+ ok = cli_conn_have_dfs(rootcli);
+ if (!ok) {
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+ }
+
+ *targetcli = NULL;
+
+ is_already_dfs = cli_dfs_is_already_full_path(rootcli, path);
+ if (is_already_dfs) {
+ const char *localpath = NULL;
+ /*
+ * Given path is already converted to DFS.
+ * Convert to a local path so clean_path()
+ * can correctly strip any wildcards.
+ */
+ status = cli_dfs_target_check(ctx,
+ rootcli,
+ path,
+ &localpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ path = localpath;
+ }
+
+ /* Send a trans2_query_path_info to check for a referral. */
+
+ cleanpath = clean_path(ctx, path);
+ if (!cleanpath) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath);
+ if (!dfs_path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes);
+ if (NT_STATUS_IS_OK(status)) {
+ /* This is an ordinary path, just return it. */
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ goto done;
+ }
+
+ /* Special case where client asked for a path that does not exist */
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ *targetcli = rootcli;
+ *pp_targetpath = talloc_strdup(ctx, path);
+ if (!*pp_targetpath) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ goto done;
+ }
+
+ /* We got an error, check for DFS referral. */
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
+ return status;
+ }
+
+ /* Check for the referral. */
+
+ status = cli_cm_open(ctx,
+ rootcli,
+ smbXcli_conn_remote_name(rootcli->conn),
+ "IPC$",
+ creds,
+ NULL, /* dest_ss not needed, we reuse the transport */
+ 0,
+ 0x20,
+ &cli_ipc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
+ &num_refs, &consumed);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!num_refs || !refs[0].dfspath) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /*
+ * Bug#10123 - DFS referral entries can be provided in a random order,
+ * so check the connection cache for each item to avoid unnecessary
+ * reconnections.
+ */
+ dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs);
+ if (dfs_refs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (count = 0; count < num_refs; count++) {
+ if (!split_dfs_path(dfs_refs, refs[count].dfspath,
+ &dfs_refs[count].server,
+ &dfs_refs[count].share,
+ &dfs_refs[count].extrapath)) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ccli = cli_cm_find(rootcli, dfs_refs[count].server,
+ dfs_refs[count].share);
+ if (ccli != NULL) {
+ extrapath = dfs_refs[count].extrapath;
+ *targetcli = ccli;
+ break;
+ }
+ }
+
+ /*
+ * If no cached connection was found, then connect to the first live
+ * referral server in the list.
+ */
+ for (count = 0; (ccli == NULL) && (count < num_refs); count++) {
+ /* Connect to the target server & share */
+ status = cli_cm_connect(ctx, rootcli,
+ dfs_refs[count].server,
+ dfs_refs[count].share,
+ creds,
+ NULL, /* dest_ss */
+ 0, /* port */
+ 0x20,
+ targetcli);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
+ dfs_refs[count].server,
+ dfs_refs[count].share);
+ continue;
+ } else {
+ extrapath = dfs_refs[count].extrapath;
+ break;
+ }
+ }
+
+ /* No available referral server for the connection */
+ if (*targetcli == NULL) {
+ TALLOC_FREE(dfs_refs);
+ return status;
+ }
+
+ /* Make sure to recreate the original string including any wildcards. */
+
+ dfs_path = cli_dfs_make_full_path(ctx, rootcli, path);
+ if (!dfs_path) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NO_MEMORY;
+ }
+ pathlen = strlen(dfs_path);
+ consumed = MIN(pathlen, consumed);
+ *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]);
+ if (!*pp_targetpath) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NO_MEMORY;
+ }
+ dfs_path[consumed] = '\0';
+
+ /*
+ * *pp_targetpath is now the unconsumed part of the path.
+ * dfs_path is now the consumed part of the path
+ * (in \server\share\path format).
+ */
+
+ if (extrapath && strlen(extrapath) > 0) {
+ /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */
+ /* put the trailing \ on the path, so to be safe we put one in if needed */
+ if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') {
+ *pp_targetpath = talloc_asprintf(ctx,
+ "%s\\%s",
+ extrapath,
+ *pp_targetpath);
+ } else {
+ *pp_targetpath = talloc_asprintf(ctx,
+ "%s%s",
+ extrapath,
+ *pp_targetpath);
+ }
+ if (!*pp_targetpath) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* parse out the consumed mount path */
+ /* trim off the \server\share\ */
+
+ ppath = dfs_path;
+
+ if (*ppath != '\\') {
+ d_printf("cli_resolve_path: "
+ "dfs_path (%s) not in correct format.\n",
+ dfs_path );
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ppath++; /* Now pointing at start of server name. */
+
+ if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ppath++; /* Now pointing at start of share name. */
+
+ if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ ppath++; /* Now pointing at path component. */
+
+ newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath );
+ if (!newmount) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ /* Check for another dfs referral, note that we are not
+ checking for loops here. */
+
+ if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) {
+ status = cli_resolve_path(ctx,
+ newmount,
+ creds,
+ *targetcli,
+ *pp_targetpath,
+ &newcli,
+ &newpath);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * When cli_resolve_path returns true here it's always
+ * returning the complete path in newpath, so we're done
+ * here.
+ */
+ *targetcli = newcli;
+ *pp_targetpath = newpath;
+ TALLOC_FREE(dfs_refs);
+ return status;
+ }
+ }
+
+ done:
+
+ if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) {
+ target_tcon = (*targetcli)->smb2.tcon;
+ } else {
+ target_tcon = (*targetcli)->smb1.tcon;
+ }
+
+ /* If returning true ensure we return a dfs root full path. */
+ if (smbXcli_tcon_is_dfs_share(target_tcon)) {
+ dfs_path = talloc_strdup(ctx, *pp_targetpath);
+ if (!dfs_path) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NO_MEMORY;
+ }
+ *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path);
+ if (*pp_targetpath == NULL) {
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ TALLOC_FREE(dfs_refs);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *sharename,
+ char **pp_newserver,
+ char **pp_newshare,
+ struct cli_credentials *creds)
+{
+ struct client_dfs_referral *refs = NULL;
+ size_t num_refs = 0;
+ size_t consumed = 0;
+ char *fullpath = NULL;
+ bool res;
+ struct smbXcli_tcon *orig_tcon = NULL;
+ char *orig_share = NULL;
+ char *newextrapath = NULL;
+ NTSTATUS status;
+ const char *remote_name;
+ enum smb_encryption_setting encryption_state =
+ cli_credentials_get_smb_encryption(creds);
+
+ if (!cli || !sharename) {
+ return false;
+ }
+
+ remote_name = smbXcli_conn_remote_name(cli->conn);
+
+ /* special case. never check for a referral on the IPC$ share */
+
+ if (strequal(sharename, "IPC$")) {
+ return false;
+ }
+
+ /* send a trans2_query_path_info to check for a referral */
+
+ fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename);
+ if (!fullpath) {
+ return false;
+ }
+
+ /* Store tcon state. */
+ if (cli_state_has_tcon(cli)) {
+ cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
+ }
+
+ /* check for the referral */
+
+ if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) {
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ return false;
+ }
+
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ status = cli_cm_force_encryption_creds(cli, creds, "IPC$");
+ if (!NT_STATUS_IS_OK(status)) {
+ switch (encryption_state) {
+ case SMB_ENCRYPTION_DESIRED:
+ break;
+ case SMB_ENCRYPTION_REQUIRED:
+ default:
+ /*
+ * Failed to set up encryption.
+ * Disconnect the temporary IPC$
+ * tcon before restoring the original
+ * tcon so we don't leak it.
+ */
+ cli_tdis(cli);
+ cli_state_restore_tcon_share(cli,
+ orig_tcon,
+ orig_share);
+ return false;
+ }
+ }
+ }
+
+ status = cli_dfs_get_referral(ctx, cli, fullpath, &refs,
+ &num_refs, &consumed);
+ res = NT_STATUS_IS_OK(status);
+
+ status = cli_tdis(cli);
+
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!res || !num_refs) {
+ return false;
+ }
+
+ if (!refs[0].dfspath) {
+ return false;
+ }
+
+ if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
+ pp_newshare, &newextrapath)) {
+ return false;
+ }
+
+ /* check that this is not a self-referral */
+
+ if (strequal(remote_name, *pp_newserver) &&
+ strequal(sharename, *pp_newshare)) {
+ return false;
+ }
+
+ return true;
+}
+
+/********************************************************************
+ Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS
+ path for the targets of rename and hardlink. If we have been given
+ a DFS path for these calls, convert it back into a local path by
+ stripping off the DFS prefix.
+********************************************************************/
+
+NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ const char *fname_dst,
+ const char **fname_dst_out)
+{
+ char *dfs_prefix = NULL;
+ size_t prefix_len = 0;
+ struct smbXcli_tcon *tcon = NULL;
+
+ if (!smbXcli_conn_dfs_supported(cli->conn)) {
+ goto copy_fname_out;
+ }
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ tcon = cli->smb2.tcon;
+ } else {
+ tcon = cli->smb1.tcon;
+ }
+ if (!smbXcli_tcon_is_dfs_share(tcon)) {
+ goto copy_fname_out;
+ }
+ dfs_prefix = cli_dfs_make_full_path(mem_ctx, cli, "");
+ if (dfs_prefix == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ prefix_len = strlen(dfs_prefix);
+ if (strncmp(fname_dst, dfs_prefix, prefix_len) != 0) {
+ /*
+ * Prefix doesn't match. Assume it was
+ * already stripped or not added in the
+ * first place.
+ */
+ goto copy_fname_out;
+ }
+ /* Return the trailing name after the prefix. */
+ *fname_dst_out = &fname_dst[prefix_len];
+ TALLOC_FREE(dfs_prefix);
+ return NT_STATUS_OK;
+
+ copy_fname_out:
+
+ /*
+ * No change to the destination name. Just
+ * point it at the incoming destination name.
+ */
+ *fname_dst_out = fname_dst;
+ TALLOC_FREE(dfs_prefix);
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ Convert a pathname into a DFS path if it hasn't already been converted.
+ Always returns a talloc'ed path, makes it easy to pass const paths in.
+********************************************************************/
+
+char *smb1_dfs_share_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path)
+{
+ bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
+ smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ bool is_already_dfs_path = false;
+ bool posix = (cli->requested_posix_capabilities &
+ CIFS_UNIX_POSIX_PATHNAMES_CAP);
+ char sepchar = (posix ? '/' : '\\');
+
+ if (!is_dfs) {
+ return talloc_strdup(ctx, path);
+ }
+ is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
+ if (is_already_dfs_path) {
+ return talloc_strdup(ctx, path);
+ }
+ /*
+ * We don't use cli_dfs_make_full_path() as,
+ * when given a null path, cli_dfs_make_full_path
+ * deliberately adds a trailing '\\' (this is by
+ * design to check for an existing DFS prefix match).
+ */
+ if (path[0] == '\0') {
+ return talloc_asprintf(ctx,
+ "%c%s%c%s",
+ sepchar,
+ smbXcli_conn_remote_name(cli->conn),
+ sepchar,
+ cli->share);
+ }
+ while (*path == sepchar) {
+ path++;
+ }
+ return talloc_asprintf(ctx,
+ "%c%s%c%s%c%s",
+ sepchar,
+ smbXcli_conn_remote_name(cli->conn),
+ sepchar,
+ cli->share,
+ sepchar,
+ path);
+}
diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c
new file mode 100644
index 0000000..a45bdac
--- /dev/null
+++ b/source3/libsmb/clidgram.c
@@ -0,0 +1,482 @@
+/*
+ Unix SMB/CIFS implementation.
+ client dgram calls
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "libsmb/clidgram.h"
+#include "libsmb/nmblib.h"
+#include "libsmb/unexpected.h"
+#include "messages.h"
+#include "librpc/gen_ndr/samr.h"
+#include "../lib/util/pidfile.h"
+#include "lib/util/string_wrappers.h"
+
+/*
+ * cli_send_mailslot, send a mailslot for client code ...
+ */
+
+static bool cli_prep_mailslot(bool unique, const char *mailslot,
+ uint16_t priority,
+ char *buf, int len,
+ const char *srcname, int src_type,
+ const char *dstname, int dest_type,
+ const struct sockaddr_storage *dest_ss,
+ int dgm_id,
+ struct packet_struct *p)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *ptr, *p2;
+ char tmp[4];
+ char addr[INET6_ADDRSTRLEN];
+
+ ZERO_STRUCTP(p);
+
+ /*
+ * Next, build the DGRAM ...
+ */
+
+ /* DIRECT GROUP or UNIQUE datagram. */
+ dgram->header.msg_type = unique ? 0x10 : 0x11;
+ dgram->header.flags.node_type = M_NODE;
+ dgram->header.flags.first = True;
+ dgram->header.flags.more = False;
+ dgram->header.dgm_id = dgm_id;
+ /* source ip is filled by nmbd */
+ dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */
+ dgram->header.packet_offset = 0;
+
+ make_nmb_name(&dgram->source_name,srcname,src_type);
+ make_nmb_name(&dgram->dest_name,dstname,dest_type);
+
+ ptr = &dgram->data[0];
+
+ /* Setup the smb part. */
+ ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */
+ memcpy(tmp,ptr,4);
+
+ if (smb_size + 17*2 + strlen(mailslot) + 1 + len > MAX_DGRAM_SIZE) {
+ DEBUG(0, ("cli_send_mailslot: Cannot write beyond end of packet\n"));
+ return False;
+ }
+
+ cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True);
+ memcpy(ptr,tmp,4);
+
+ SCVAL(ptr,smb_com,SMBtrans);
+ SSVAL(ptr,smb_vwv1,len);
+ SSVAL(ptr,smb_vwv11,len);
+ SSVAL(ptr,smb_vwv12,70 + strlen(mailslot));
+ SSVAL(ptr,smb_vwv13,3);
+ SSVAL(ptr,smb_vwv14,1);
+ SSVAL(ptr,smb_vwv15,priority);
+ SSVAL(ptr,smb_vwv16,2);
+ p2 = smb_buf(ptr);
+ fstrcpy(p2,mailslot);
+ p2 = skip_string(ptr,MAX_DGRAM_SIZE,p2);
+ if (!p2) {
+ return False;
+ }
+
+ memcpy(p2,buf,len);
+ p2 += len;
+
+ dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */
+
+ p->packet_type = DGRAM_PACKET;
+ p->ip = ((const struct sockaddr_in *)dest_ss)->sin_addr;
+ p->timestamp = time(NULL);
+
+ DEBUG(4,("send_mailslot: Sending to mailslot %s from %s ",
+ mailslot, nmb_namestr(&dgram->source_name)));
+ print_sockaddr(addr, sizeof(addr), dest_ss);
+
+ DEBUGADD(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), addr));
+
+ return true;
+}
+
+static char *mailslot_name(TALLOC_CTX *mem_ctx, struct in_addr dc_ip)
+{
+ return talloc_asprintf(mem_ctx, "%s%X",
+ NBT_MAILSLOT_GETDC, dc_ip.s_addr);
+}
+
+static bool prep_getdc_request(const struct sockaddr_storage *dc_ss,
+ const char *account_name,
+ uint32_t account_flags,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ uint32_t nt_version,
+ const char *my_mailslot,
+ int dgm_id,
+ struct packet_struct *p)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct nbt_netlogon_packet packet;
+ struct NETLOGON_SAM_LOGON_REQUEST *s;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob = data_blob_null;
+ struct dom_sid my_sid;
+ bool ret = false;
+
+ ZERO_STRUCT(packet);
+ ZERO_STRUCT(my_sid);
+
+ if (sid != NULL) {
+ my_sid = *sid;
+ }
+
+ packet.command = LOGON_SAM_LOGON_REQUEST;
+ s = &packet.req.logon;
+
+ s->request_count = 0;
+ s->computer_name = lp_netbios_name();
+ s->user_name = account_name;
+ s->mailslot_name = my_mailslot;
+ s->acct_control = account_flags;
+ s->sid = my_sid;
+ s->nt_version = nt_version;
+ s->lmnt_token = 0xffff;
+ s->lm20_token = 0xffff;
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(nbt_netlogon_packet, &packet);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &packet,
+ (ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ goto fail;
+ }
+
+ ret = cli_prep_mailslot(false, NBT_MAILSLOT_NTLOGON, 0,
+ (char *)blob.data, blob.length,
+ lp_netbios_name(), 0, domain_name, 0x1c,
+ dc_ss, dgm_id, p);
+fail:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static bool parse_getdc_response(
+ struct packet_struct *packet,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ uint32_t *nt_version,
+ const char **dc_name,
+ struct netlogon_samlogon_response **samlogon_response)
+{
+ DATA_BLOB blob;
+ struct netlogon_samlogon_response *r;
+ union dgram_message_body p;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ const char *returned_dc = NULL;
+ const char *returned_domain = NULL;
+
+ blob = data_blob_const(packet->packet.dgram.data,
+ packet->packet.dgram.datasize);
+ if (blob.length < 4) {
+ DEBUG(1, ("invalid length: %d\n", (int)blob.length));
+ return false;
+ }
+
+ if (RIVAL(blob.data,0) != DGRAM_SMB) {
+ DEBUG(1, ("invalid packet\n"));
+ return false;
+ }
+
+ blob.data += 4;
+ blob.length -= 4;
+
+ ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB,
+ (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(1, ("failed to parse packet\n"));
+ return false;
+ }
+
+ if (p.smb.smb_command != SMB_TRANSACTION) {
+ DEBUG(1, ("invalid smb_command: %d\n", p.smb.smb_command));
+ return false;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(dgram_smb_packet, &p);
+ }
+
+ blob = p.smb.body.trans.data;
+
+ r = talloc_zero(mem_ctx, struct netlogon_samlogon_response);
+ if (!r) {
+ return false;
+ }
+
+ status = pull_netlogon_samlogon_response(&blob, r, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(r);
+ return false;
+ }
+
+ map_netlogon_samlogon_response(r);
+
+ /* do we still need this ? */
+ *nt_version = r->ntver;
+
+ returned_domain = r->data.nt5_ex.domain_name;
+ returned_dc = r->data.nt5_ex.pdc_name;
+
+ if (!strequal(returned_domain, domain_name)) {
+ DEBUG(3, ("GetDC: Expected domain %s, got %s\n",
+ domain_name, returned_domain));
+ TALLOC_FREE(r);
+ return false;
+ }
+
+ if (*returned_dc == '\\') returned_dc += 1;
+ if (*returned_dc == '\\') returned_dc += 1;
+
+ *dc_name = talloc_strdup(mem_ctx, returned_dc);
+ if (!*dc_name) {
+ TALLOC_FREE(r);
+ return false;
+ }
+
+ if (samlogon_response) {
+ *samlogon_response = r;
+ } else {
+ TALLOC_FREE(r);
+ }
+
+ DEBUG(10, ("GetDC gave name %s for domain %s\n",
+ *dc_name, returned_domain));
+
+ return True;
+}
+
+struct nbt_getdc_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ struct nb_packet_reader *reader;
+ const char *my_mailslot;
+ pid_t nmbd_pid;
+
+ const struct sockaddr_storage *dc_addr;
+ const char *domain_name;
+ const struct dom_sid *sid;
+ uint32_t nt_version;
+ const char *dc_name;
+ struct netlogon_samlogon_response *samlogon_response;
+
+ struct packet_struct p;
+};
+
+static void nbt_getdc_got_reader(struct tevent_req *subreq);
+static void nbt_getdc_got_response(struct tevent_req *subreq);
+
+struct tevent_req *nbt_getdc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ const struct sockaddr_storage *dc_addr,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ const char *account_name,
+ uint32_t account_flags,
+ uint32_t nt_version)
+{
+ struct tevent_req *req, *subreq;
+ struct nbt_getdc_state *state;
+ uint16_t dgm_id;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct nbt_getdc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->msg_ctx = msg_ctx;
+ state->dc_addr = dc_addr;
+ state->domain_name = domain_name;
+ state->sid = sid;
+ state->nt_version = nt_version;
+
+ if (dc_addr->ss_family != AF_INET) {
+ tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+ return tevent_req_post(req, ev);
+ }
+ state->my_mailslot = mailslot_name(
+ state, ((const struct sockaddr_in *)dc_addr)->sin_addr);
+ if (tevent_req_nomem(state->my_mailslot, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->nmbd_pid = pidfile_pid(lp_pid_directory(), "nmbd");
+ if (state->nmbd_pid == 0) {
+ DEBUG(3, ("No nmbd found\n"));
+ tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+ return tevent_req_post(req, ev);
+ }
+
+ generate_random_buffer((uint8_t *)(void *)&dgm_id, sizeof(dgm_id));
+
+ ok = prep_getdc_request(dc_addr, account_name, account_flags,
+ domain_name, sid, nt_version,
+ state->my_mailslot, dgm_id & 0x7fff,
+ &state->p);
+
+ if (!ok) {
+ DEBUG(3, ("prep_getdc_request failed\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = nb_packet_reader_send(state, ev, DGRAM_PACKET, -1,
+ state->my_mailslot);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nbt_getdc_got_reader, req);
+ return req;
+}
+
+static void nbt_getdc_got_reader(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nbt_getdc_state *state = tevent_req_data(
+ req, struct nbt_getdc_state);
+ NTSTATUS status;
+
+ status = nb_packet_reader_recv(subreq, state, &state->reader);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("nb_packet_reader_recv returned %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ status = messaging_send_buf(
+ state->msg_ctx, pid_to_procid(state->nmbd_pid),
+ MSG_SEND_PACKET, (uint8_t *)&state->p, sizeof(state->p));
+
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("messaging_send_buf returned %s\n",
+ nt_errstr(status)));
+ return;
+ }
+ subreq = nb_packet_read_send(state, state->ev, state->reader);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nbt_getdc_got_response, req);
+}
+
+static void nbt_getdc_got_response(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nbt_getdc_state *state = tevent_req_data(
+ req, struct nbt_getdc_state);
+ struct packet_struct *p;
+ NTSTATUS status;
+ bool ret;
+
+ status = nb_packet_read_recv(subreq, state, &p);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ret = parse_getdc_response(p, state, state->domain_name,
+ &state->nt_version, &state->dc_name,
+ &state->samlogon_response);
+ if (!ret) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS nbt_getdc_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *nt_version, const char **dc_name,
+ struct netlogon_samlogon_response **samlogon_response)
+{
+ struct nbt_getdc_state *state = tevent_req_data(
+ req, struct nbt_getdc_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (nt_version != NULL) {
+ *nt_version = state->nt_version;
+ }
+ if (dc_name != NULL) {
+ *dc_name = talloc_move(mem_ctx, &state->dc_name);
+ }
+ if (samlogon_response != NULL) {
+ *samlogon_response = talloc_move(
+ mem_ctx, &state->samlogon_response);
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS nbt_getdc(struct messaging_context *msg_ctx,
+ uint32_t timeout_in_seconds,
+ const struct sockaddr_storage *dc_addr,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ const char *account_name,
+ uint32_t account_flags,
+ uint32_t nt_version,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnt_version,
+ const char **dc_name,
+ struct netlogon_samlogon_response **samlogon_response)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = nbt_getdc_send(ev, ev, msg_ctx, dc_addr, domain_name,
+ sid, account_name, account_flags, nt_version);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_set_endtime(req, ev,
+ timeval_current_ofs(timeout_in_seconds, 0))) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = nbt_getdc_recv(req, mem_ctx, pnt_version, dc_name,
+ samlogon_response);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clidgram.h b/source3/libsmb/clidgram.h
new file mode 100644
index 0000000..46e11f4
--- /dev/null
+++ b/source3/libsmb/clidgram.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+ client dgram calls
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Richard Sharpe 2001
+ Copyright (C) John Terpstra 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB_CLIDGRAM_H_
+#define _LIBSMB_CLIDGRAM_H_
+
+#include "../libcli/netlogon/netlogon.h"
+
+/* The following definitions come from libsmb/clidgram.c */
+
+struct tevent_req *nbt_getdc_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ const struct sockaddr_storage *dc_addr,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ const char *account_name,
+ uint32_t account_flags,
+ uint32_t nt_version);
+NTSTATUS nbt_getdc_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *nt_version, const char **dc_name,
+ struct netlogon_samlogon_response **samlogon_response);
+NTSTATUS nbt_getdc(struct messaging_context *msg_ctx,
+ uint32_t timeout_in_seconds,
+ const struct sockaddr_storage *dc_addr,
+ const char *domain_name,
+ const struct dom_sid *sid,
+ const char *account_name,
+ uint32_t account_flags,
+ uint32_t nt_version,
+ TALLOC_CTX *mem_ctx,
+ uint32_t *pnt_version,
+ const char **dc_name,
+ struct netlogon_samlogon_response **samlogon_response);
+
+#endif /* _LIBSMB_CLIDGRAM_H_ */
diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c
new file mode 100644
index 0000000..bec1184
--- /dev/null
+++ b/source3/libsmb/clientgen.c
@@ -0,0 +1,650 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client generic functions
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "../libcli/smb/smb_signing.h"
+#include "../libcli/smb/smb_seal.h"
+#include "async_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
+#include "../librpc/ndr/libndr.h"
+#include "../include/client.h"
+
+/****************************************************************************
+ Change the timeout (in milliseconds).
+****************************************************************************/
+
+unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout)
+{
+ unsigned int old_timeout = cli->timeout;
+ DBG_DEBUG("Changing connection timeout for server '%s' from %d (ms) to "
+ "%d (ms).\n",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->timeout,
+ timeout);
+ cli->timeout = timeout;
+ return old_timeout;
+}
+
+/****************************************************************************
+ Set the 'backup_intent' flag.
+****************************************************************************/
+
+bool cli_set_backup_intent(struct cli_state *cli, bool flag)
+{
+ bool old_state = cli->backup_intent;
+ cli->backup_intent = flag;
+ return old_state;
+}
+
+/****************************************************************************
+ Initialise a client structure. Always returns a talloc'ed struct.
+ Set the signing state (used from the command line).
+****************************************************************************/
+
+struct GUID cli_state_client_guid;
+
+struct cli_state *cli_state_create(TALLOC_CTX *mem_ctx,
+ int fd,
+ const char *remote_name,
+ enum smb_signing_setting signing_state,
+ int flags)
+{
+ struct cli_state *cli = NULL;
+ bool use_spnego = lp_client_use_spnego();
+ bool force_dos_errors = false;
+ bool force_ascii = false;
+ bool use_level_II_oplocks = false;
+ uint32_t smb1_capabilities = 0;
+ uint32_t smb2_capabilities = 0;
+ struct smb311_capabilities smb3_capabilities =
+ smb311_capabilities_parse("client",
+ lp_client_smb3_signing_algorithms(),
+ lp_client_smb3_encryption_algorithms());
+ struct GUID client_guid;
+
+ if (!GUID_all_zero(&cli_state_client_guid)) {
+ client_guid = cli_state_client_guid;
+ } else {
+ const char *str = NULL;
+
+ str = lp_parm_const_string(-1, "libsmb", "client_guid", NULL);
+ if (str != NULL) {
+ GUID_from_string(str, &client_guid);
+ } else {
+ client_guid = GUID_random();
+ }
+ }
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ DEBUG(0,("libsmb based programs must *NOT* be setuid root.\n"));
+ return NULL;
+ }
+
+ cli = talloc_zero(mem_ctx, struct cli_state);
+ if (!cli) {
+ return NULL;
+ }
+
+ cli->server_domain = talloc_strdup(cli, "");
+ if (!cli->server_domain) {
+ goto error;
+ }
+ cli->server_os = talloc_strdup(cli, "");
+ if (!cli->server_os) {
+ goto error;
+ }
+ cli->server_type = talloc_strdup(cli, "");
+ if (!cli->server_type) {
+ goto error;
+ }
+
+ cli->raw_status = NT_STATUS_INTERNAL_ERROR;
+ cli->map_dos_errors = true; /* remove this */
+ cli->timeout = CLIENT_TIMEOUT;
+
+ /* Set the CLI_FORCE_DOSERR environment variable to test
+ client routines using DOS errors instead of STATUS32
+ ones. This intended only as a temporary hack. */
+ if (getenv("CLI_FORCE_DOSERR")) {
+ force_dos_errors = true;
+ }
+ if (flags & CLI_FULL_CONNECTION_FORCE_DOS_ERRORS) {
+ force_dos_errors = true;
+ }
+
+ if (getenv("CLI_FORCE_ASCII")) {
+ force_ascii = true;
+ }
+ if (!lp_unicode()) {
+ force_ascii = true;
+ }
+ if (flags & CLI_FULL_CONNECTION_FORCE_ASCII) {
+ force_ascii = true;
+ }
+
+ if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO) {
+ use_spnego = false;
+ }
+
+ if (flags & CLI_FULL_CONNECTION_OPLOCKS) {
+ cli->use_oplocks = true;
+ }
+ if (flags & CLI_FULL_CONNECTION_LEVEL_II_OPLOCKS) {
+ use_level_II_oplocks = true;
+ }
+
+ if (signing_state == SMB_SIGNING_IPC_DEFAULT) {
+ /*
+ * Ensure for IPC/RPC the default is to require
+ * signing unless explicitly turned off by the
+ * administrator.
+ */
+ signing_state = lp_client_ipc_signing();
+ }
+
+ if (signing_state == SMB_SIGNING_DEFAULT) {
+ signing_state = lp_client_signing();
+ }
+
+ smb1_capabilities = 0;
+ smb1_capabilities |= CAP_LARGE_FILES;
+ smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS;
+ smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND;
+ smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS;
+ smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX;
+ smb1_capabilities |= CAP_LWIO;
+
+ if (!force_dos_errors) {
+ smb1_capabilities |= CAP_STATUS32;
+ }
+
+ if (!force_ascii) {
+ smb1_capabilities |= CAP_UNICODE;
+ }
+
+ if (use_spnego) {
+ smb1_capabilities |= CAP_EXTENDED_SECURITY;
+ }
+
+ if (use_level_II_oplocks) {
+ smb1_capabilities |= CAP_LEVEL_II_OPLOCKS;
+ }
+
+ smb2_capabilities = SMB2_CAP_ALL;
+
+ cli->conn = smbXcli_conn_create(cli, fd, remote_name,
+ signing_state,
+ smb1_capabilities,
+ &client_guid,
+ smb2_capabilities,
+ &smb3_capabilities);
+ if (cli->conn == NULL) {
+ goto error;
+ }
+
+ cli->smb1.pid = (uint32_t)getpid();
+ cli->smb1.vc_num = cli->smb1.pid;
+ cli->smb1.session = smbXcli_session_create(cli, cli->conn);
+ if (cli->smb1.session == NULL) {
+ goto error;
+ }
+
+ cli->initialised = 1;
+ return cli;
+
+ /* Clean up after malloc() error */
+
+ error:
+
+ TALLOC_FREE(cli);
+ return NULL;
+}
+
+/****************************************************************************
+ Close all pipes open on this session.
+****************************************************************************/
+
+static void cli_nt_pipes_close(struct cli_state *cli)
+{
+ while (cli->pipe_list != NULL) {
+ /*
+ * No TALLOC_FREE here!
+ */
+ talloc_free(cli->pipe_list);
+ }
+}
+
+/****************************************************************************
+ Shutdown a client structure.
+****************************************************************************/
+
+static void _cli_shutdown(struct cli_state *cli)
+{
+ cli_nt_pipes_close(cli);
+
+ /*
+ * tell our peer to free his resources. Without 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_state_has_tcon(cli)) {
+ cli_tdis(cli);
+ }
+
+ smbXcli_conn_disconnect(cli->conn, NT_STATUS_OK);
+
+ TALLOC_FREE(cli);
+}
+
+void cli_shutdown(struct cli_state *cli)
+{
+ struct cli_state *cli_head;
+ if (cli == NULL) {
+ return;
+ }
+ DLIST_HEAD(cli, cli_head);
+ if (cli_head == cli) {
+ /*
+ * head of a DFS list, shutdown all subsidiary DFS
+ * connections.
+ */
+ struct cli_state *p, *next;
+
+ for (p = cli_head->next; p; p = next) {
+ next = p->next;
+ DLIST_REMOVE(cli_head, p);
+ _cli_shutdown(p);
+ }
+ } else {
+ DLIST_REMOVE(cli_head, cli);
+ }
+
+ _cli_shutdown(cli);
+}
+
+uint16_t cli_state_get_vc_num(struct cli_state *cli)
+{
+ return cli->smb1.vc_num;
+}
+
+/****************************************************************************
+ Set the PID to use for smb messages. Return the old pid.
+****************************************************************************/
+
+uint32_t cli_setpid(struct cli_state *cli, uint32_t pid)
+{
+ uint32_t ret = cli->smb1.pid;
+ cli->smb1.pid = pid;
+ return ret;
+}
+
+uint32_t cli_getpid(struct cli_state *cli)
+{
+ return cli->smb1.pid;
+}
+
+bool cli_state_is_encryption_on(struct cli_state *cli)
+{
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return smb1cli_conn_encryption_on(cli->conn);
+ }
+
+ if (cli->smb2.tcon == NULL) {
+ return false;
+ }
+
+ return smb2cli_tcon_is_encryption_on(cli->smb2.tcon);
+}
+
+bool cli_state_has_tcon(struct cli_state *cli)
+{
+ uint32_t tid;
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ if (cli->smb2.tcon == NULL) {
+ return false;
+ }
+ tid = cli_state_get_tid(cli);
+ if (tid == UINT32_MAX) {
+ return false;
+ }
+ } else {
+ if (cli->smb1.tcon == NULL) {
+ return false;
+ }
+ tid = cli_state_get_tid(cli);
+ if (tid == UINT16_MAX) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t cli_state_get_tid(struct cli_state *cli)
+{
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return smb2cli_tcon_current_id(cli->smb2.tcon);
+ } else {
+ return (uint32_t)smb1cli_tcon_current_id(cli->smb1.tcon);
+ }
+}
+
+uint32_t cli_state_set_tid(struct cli_state *cli, uint32_t tid)
+{
+ uint32_t ret;
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ ret = smb2cli_tcon_current_id(cli->smb2.tcon);
+ smb2cli_tcon_set_id(cli->smb2.tcon, tid);
+ } else {
+ ret = smb1cli_tcon_current_id(cli->smb1.tcon);
+ smb1cli_tcon_set_id(cli->smb1.tcon, tid);
+ }
+ return ret;
+}
+
+static struct smbXcli_tcon *cli_state_save_tcon(struct cli_state *cli)
+{
+ /*
+ * Note. This used to make a deep copy of either
+ * cli->smb2.tcon or cli->smb1.tcon, but this leaves
+ * the original pointer in place which will then get
+ * TALLOC_FREE()'d when the new connection is made on
+ * this cli_state.
+ *
+ * As there may be pipes open on the old connection with
+ * talloc'ed state allocated using the tcon pointer as a
+ * parent we can't deep copy and then free this as that
+ * closes the open pipes.
+ *
+ * This call is used to temporarily swap out a tcon pointer
+ * to allow a new tcon on the same cli_state.
+ *
+ * Just return the raw pointer and set the old value to NULL.
+ * We know we MUST be calling cli_state_restore_tcon() below
+ * to restore before closing the session.
+ *
+ * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=13992
+ */
+ struct smbXcli_tcon *tcon_ret = NULL;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ tcon_ret = cli->smb2.tcon;
+ cli->smb2.tcon = NULL; /* *Not* TALLOC_FREE(). */
+ } else {
+ tcon_ret = cli->smb1.tcon;
+ cli->smb1.tcon = NULL; /* *Not* TALLOC_FREE(). */
+ }
+ return tcon_ret;
+}
+
+void cli_state_save_tcon_share(struct cli_state *cli,
+ struct smbXcli_tcon **_tcon_ret,
+ char **_sharename_ret)
+{
+ *_tcon_ret = cli_state_save_tcon(cli);
+ /*
+ * No talloc_copy as cli->share is already
+ * allocated off cli.
+ */
+ *_sharename_ret = cli->share;
+ cli->share = NULL;
+}
+
+static void cli_state_restore_tcon(struct cli_state *cli,
+ struct smbXcli_tcon *tcon)
+{
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ TALLOC_FREE(cli->smb2.tcon);
+ cli->smb2.tcon = tcon;
+ } else {
+ TALLOC_FREE(cli->smb1.tcon);
+ cli->smb1.tcon = tcon;
+ }
+}
+
+void cli_state_restore_tcon_share(struct cli_state *cli,
+ struct smbXcli_tcon *tcon,
+ char *share)
+{
+ /* cli->share will have been replaced by a cli_tree_connect() call. */
+ TALLOC_FREE(cli->share);
+ cli->share = share;
+ cli_state_restore_tcon(cli, tcon);
+}
+
+uint16_t cli_state_get_uid(struct cli_state *cli)
+{
+ return smb1cli_session_current_id(cli->smb1.session);
+}
+
+uint16_t cli_state_set_uid(struct cli_state *cli, uint16_t uid)
+{
+ uint16_t ret = smb1cli_session_current_id(cli->smb1.session);
+ smb1cli_session_set_id(cli->smb1.session, uid);
+ return ret;
+}
+
+/****************************************************************************
+ Set the case sensitivity flag on the packets. Returns old state.
+****************************************************************************/
+
+bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive)
+{
+ bool ret;
+ uint32_t fs_attrs;
+ struct smbXcli_tcon *tcon;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ tcon = cli->smb2.tcon;
+ } else {
+ tcon = cli->smb1.tcon;
+ }
+
+ fs_attrs = smbXcli_tcon_get_fs_attributes(tcon);
+ if (fs_attrs & FILE_CASE_SENSITIVE_SEARCH) {
+ ret = true;
+ } else {
+ ret = false;
+ }
+ if (case_sensitive) {
+ fs_attrs |= FILE_CASE_SENSITIVE_SEARCH;
+ } else {
+ fs_attrs &= ~FILE_CASE_SENSITIVE_SEARCH;
+ }
+ smbXcli_tcon_set_fs_attributes(tcon, fs_attrs);
+
+ return ret;
+}
+
+uint32_t cli_state_available_size(struct cli_state *cli, uint32_t ofs)
+{
+ uint32_t ret = smb1cli_conn_max_xmit(cli->conn);
+
+ if (ofs >= ret) {
+ return 0;
+ }
+
+ ret -= ofs;
+
+ return ret;
+}
+
+time_t cli_state_server_time(struct cli_state *cli)
+{
+ NTTIME nt;
+ time_t t;
+
+ nt = smbXcli_conn_server_system_time(cli->conn);
+ t = nt_time_to_unix(nt);
+
+ return t;
+}
+
+struct cli_echo_state {
+ uint8_t dummy;
+};
+
+static void cli_echo_done1(struct tevent_req *subreq);
+static void cli_echo_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, uint16_t num_echos,
+ DATA_BLOB data)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_echo_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_echo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = smb2cli_echo_send(
+ state, ev, cli->conn, cli->timeout);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_echo_done2, req);
+ return req;
+ }
+
+ subreq = smb1cli_echo_send(
+ state, ev, cli->conn, cli->timeout, num_echos, data);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_echo_done1, req);
+
+ return req;
+}
+
+static void cli_echo_done1(struct tevent_req *subreq)
+{
+ NTSTATUS status = smb1cli_echo_recv(subreq);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_echo_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = smb2cli_echo_recv(subreq);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+/**
+ * Get the result out from an echo request
+ * @param[in] req The async_req from cli_echo_send
+ * @retval Did the server reply correctly?
+ */
+
+NTSTATUS cli_echo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/**
+ * @brief Send/Receive SMBEcho requests
+ * @param[in] mem_ctx The memory context to put the async_req on
+ * @param[in] ev The event context that will call us back
+ * @param[in] cli The connection to send the echo to
+ * @param[in] num_echos How many times do we want to get the reply?
+ * @param[in] data The data we want to get back
+ * @retval Did the server reply correctly?
+ */
+
+NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_echo_send(frame, ev, cli, num_echos, data);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_echo_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_smb(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint8_t smb_command, uint8_t additional_flags,
+ uint8_t wct, uint16_t *vwv,
+ uint32_t num_bytes, const uint8_t *bytes,
+ struct tevent_req **result_parent,
+ uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
+ uint32_t *pnum_bytes, uint8_t **pbytes)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb_send(mem_ctx, ev, cli, smb_command, additional_flags, 0,
+ wct, vwv, num_bytes, bytes);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb_recv(req, NULL, NULL, min_wct, pwct, pvwv,
+ pnum_bytes, pbytes);
+fail:
+ TALLOC_FREE(ev);
+ if (NT_STATUS_IS_OK(status) && (result_parent != NULL)) {
+ *result_parent = req;
+ }
+ return status;
+}
diff --git a/source3/libsmb/clierror.c b/source3/libsmb/clierror.c
new file mode 100644
index 0000000..afbec47
--- /dev/null
+++ b/source3/libsmb/clierror.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ client error handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003
+ Copyright (C) Jeremy Allison 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/****************************************************************************
+ Return the 32-bit NT status code from the last packet.
+****************************************************************************/
+
+NTSTATUS cli_nt_error(struct cli_state *cli)
+{
+ /* Deal with socket errors first. */
+ if (!cli_state_is_connected(cli)) {
+ return NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ if (NT_STATUS_IS_DOS(cli->raw_status)) {
+ int e_class = NT_STATUS_DOS_CLASS(cli->raw_status);
+ int code = NT_STATUS_DOS_CODE(cli->raw_status);
+ return dos_to_ntstatus(e_class, code);
+ }
+
+ return cli->raw_status;
+}
+
+int cli_status_to_errno(NTSTATUS status)
+{
+ int err;
+
+ if (NT_STATUS_IS_DOS(status)) {
+ uint8_t eclass = NT_STATUS_DOS_CLASS(status);
+ uint32_t ecode = NT_STATUS_DOS_CODE(status);
+ status = dos_to_ntstatus(eclass, ecode);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /*
+ * Legacy code from cli_errno, see Samba up to 4.13: A
+ * special case for this Vista error. Since its
+ * high-order byte isn't 0xc0, it won't match
+ * correctly in map_errno_from_nt_status().
+ */
+ err = EACCES;
+ } else {
+ err = map_errno_from_nt_status(status);
+ }
+
+ DBG_NOTICE("0x%"PRIx32" -> %d\n", NT_STATUS_V(status), err);
+
+ return err;
+}
+
+/* Return a UNIX errno appropriate for the error received in the last
+ packet. */
+
+int cli_errno(struct cli_state *cli)
+{
+ bool connected;
+ int err;
+
+ connected = cli_state_is_connected(cli);
+ if (!connected) {
+ return EPIPE;
+ }
+
+ err = cli_status_to_errno(cli->raw_status);
+ return err;
+}
+
+/* Return true if the last packet was in error */
+
+bool cli_is_error(struct cli_state *cli)
+{
+ /* A socket error is always an error. */
+ if (!cli_state_is_connected(cli)) {
+ return true;
+ }
+
+ if (NT_STATUS_IS_DOS(cli->raw_status)) {
+ /* Return error if error class in non-zero */
+ uint8_t rcls = NT_STATUS_DOS_CLASS(cli->raw_status);
+ return rcls != 0;
+ }
+
+ return NT_STATUS_IS_ERR(cli->raw_status);
+}
+
+bool cli_state_is_connected(struct cli_state *cli)
+{
+ if (cli == NULL) {
+ return false;
+ }
+
+ if (!cli->initialised) {
+ return false;
+ }
+
+ return smbXcli_conn_is_connected(cli->conn);
+}
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
new file mode 100644
index 0000000..cc1b4d4
--- /dev/null
+++ b/source3/libsmb/clifile.c
@@ -0,0 +1,7486 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file operations
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2001-2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/clirap.h"
+#include "trans2.h"
+#include "ntioctl.h"
+#include "libcli/security/security.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/smb/reparse.h"
+
+struct cli_setpathinfo_state {
+ uint16_t setup;
+ uint8_t *param;
+};
+
+static void cli_setpathinfo_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t level,
+ const char *path,
+ uint8_t *data,
+ size_t data_len)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_setpathinfo_state *state;
+ uint16_t additional_flags2 = 0;
+ char *path_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_setpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup setup word. */
+ SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ /* Setup param array. */
+ state->param = talloc_zero_array(state, uint8_t, 6);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ SSVAL(state->param, 0, level);
+
+ /* Check for DFS. */
+ path_cp = smb1_dfs_share_path(state, cli, path);
+ if (tevent_req_nomem(path_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(cli->conn),
+ path_cp,
+ strlen(path_cp)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(path) &&
+ !INFO_LEVEL_IS_UNIX(level)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_trans_send(
+ state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ additional_flags2, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ &state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ talloc_get_size(state->param), /* num param. */
+ 2, /* max returned param. */
+ data, /* data. */
+ data_len, /* num data. */
+ 0); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_setpathinfo_done, req);
+ return req;
+}
+
+static void cli_setpathinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
+ NULL, 0, NULL, NULL, 0, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_setpathinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_setpathinfo(struct cli_state *cli,
+ uint16_t level,
+ const char *path,
+ uint8_t *data,
+ size_t data_len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_setpathinfo_send(ev, ev, cli, level, path, data, data_len);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_setpathinfo_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_setfileinfo_state {
+ uint16_t setup;
+ uint8_t param[6];
+};
+
+static void cli_setfileinfo_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_setfileinfo_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t level,
+ uint8_t *data,
+ size_t data_len)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_setfileinfo_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_setfileinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETFILEINFO);
+
+ PUSH_LE_U16(state->param, 0, fnum);
+ PUSH_LE_U16(state->param, 2, level);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ &state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ 6, /* num param. */
+ 2, /* max returned param. */
+ data, /* data. */
+ data_len, /* num data. */
+ 0); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_setfileinfo_done, req);
+ return req;
+}
+
+static void cli_setfileinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(
+ subreq, /* req */
+ NULL, /* mem_ctx */
+ NULL, /* recv_flags2 */
+ NULL, /* setup */
+ 0, /* min_setup */
+ NULL, /* num_setup */
+ NULL, /* param */
+ 0, /* min_param */
+ NULL, /* num_param */
+ NULL, /* data */
+ 0, /* min_data */
+ NULL); /* num_data */
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_setfileinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ Hard/Symlink a file (UNIX extensions).
+ Creates new name (sym)linked to link_target.
+****************************************************************************/
+
+struct cli_posix_link_internal_state {
+ uint8_t *data;
+};
+
+static void cli_posix_link_internal_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_posix_link_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t level,
+ const char *link_target,
+ const char *newname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_link_internal_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_posix_link_internal_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup data array. */
+ state->data = talloc_array(state, uint8_t, 0);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->data = trans2_bytes_push_str(
+ state->data, smbXcli_conn_use_unicode(cli->conn),
+ link_target, strlen(link_target)+1, NULL);
+
+ subreq = cli_setpathinfo_send(
+ state, ev, cli, level, newname,
+ state->data, talloc_get_size(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_link_internal_done, req);
+ return req;
+}
+
+static void cli_posix_link_internal_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_posix_link_internal_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ Symlink a file (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_symlink_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_symlink_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *link_target,
+ const char *newname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_symlink_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_symlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_posix_link_internal_send(
+ mem_ctx, ev, cli, SMB_SET_FILE_UNIX_LINK, link_target, newname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_symlink_done, req);
+ return req;
+}
+
+static void cli_posix_symlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_link_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_symlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_symlink(struct cli_state *cli,
+ const char *link_target,
+ const char *newname)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_symlink_send(frame,
+ ev,
+ cli,
+ link_target,
+ newname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_symlink_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Read a POSIX symlink.
+****************************************************************************/
+
+struct cli_posix_readlink_state {
+ struct cli_state *cli;
+ char *converted;
+};
+
+static void cli_posix_readlink_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_readlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_readlink_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_readlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ subreq = cli_qpathinfo_send(
+ state,
+ ev,
+ cli,
+ fname,
+ SMB_QUERY_FILE_UNIX_LINK,
+ 1,
+ UINT16_MAX);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_readlink_done, req);
+ return req;
+}
+
+static void cli_posix_readlink_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_posix_readlink_state *state = tevent_req_data(
+ req, struct cli_posix_readlink_state);
+ NTSTATUS status;
+ uint8_t *data = NULL;
+ uint32_t num_data = 0;
+ charset_t charset;
+ size_t converted_size;
+ bool ok;
+
+ status = cli_qpathinfo_recv(subreq, state, &data, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ /*
+ * num_data is > 1, we've given 1 as minimum to cli_qpathinfo_send
+ */
+ if (data == NULL || data[num_data-1] != '\0') {
+ tevent_req_nterror(req, NT_STATUS_DATA_ERROR);
+ return;
+ }
+
+ charset = smbXcli_conn_use_unicode(state->cli->conn) ?
+ CH_UTF16LE : CH_DOS;
+
+ /* The returned data is a pushed string, not raw data. */
+ ok = convert_string_talloc(
+ state,
+ charset,
+ CH_UNIX,
+ data,
+ num_data,
+ &state->converted,
+ &converted_size);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_posix_readlink_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, char **target)
+{
+ struct cli_posix_readlink_state *state = tevent_req_data(
+ req, struct cli_posix_readlink_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *target = talloc_move(mem_ctx, &state->converted);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Hard link a file (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_hardlink_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_hardlink_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *oldname,
+ const char *newname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_hardlink_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_hardlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_posix_link_internal_send(
+ state, ev, cli, SMB_SET_FILE_UNIX_HLINK, oldname, newname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_hardlink_done, req);
+ return req;
+}
+
+static void cli_posix_hardlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_link_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_hardlink(struct cli_state *cli,
+ const char *oldname,
+ const char *newname)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_hardlink_send(frame,
+ ev,
+ cli,
+ oldname,
+ newname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_hardlink_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Do a POSIX getacl - pathname based ACL get (UNIX extensions).
+****************************************************************************/
+
+struct getacl_state {
+ uint32_t num_data;
+ uint8_t *data;
+};
+
+static void cli_posix_getacl_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_getacl_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct getacl_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct getacl_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = cli_qpathinfo_send(state, ev, cli, fname, SMB_QUERY_POSIX_ACL,
+ 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_getacl_done, req);
+ return req;
+}
+
+static void cli_posix_getacl_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct getacl_state *state = tevent_req_data(
+ req, struct getacl_state);
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &state->data,
+ &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_posix_getacl_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *prb_size,
+ char **retbuf)
+{
+ struct getacl_state *state = tevent_req_data(req, struct getacl_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *prb_size = (size_t)state->num_data;
+ *retbuf = (char *)talloc_move(mem_ctx, &state->data);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_posix_getacl(struct cli_state *cli,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ size_t *prb_size,
+ char **retbuf)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_getacl_send(frame,
+ ev,
+ cli,
+ fname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_getacl_recv(req, mem_ctx, prb_size, retbuf);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Do a POSIX setacl - pathname based ACL set (UNIX extensions).
+****************************************************************************/
+
+struct setacl_state {
+ uint8_t *data;
+};
+
+static void cli_posix_setacl_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_setacl_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ const void *data,
+ size_t num_data)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct setacl_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct setacl_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->data = talloc_memdup(state, data, num_data);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_setpathinfo_send(state,
+ ev,
+ cli,
+ SMB_SET_POSIX_ACL,
+ fname,
+ state->data,
+ num_data);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_setacl_done, req);
+ return req;
+}
+
+static void cli_posix_setacl_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_setacl_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_setacl(struct cli_state *cli,
+ const char *fname,
+ const void *acl_buf,
+ size_t acl_buf_size)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_setacl_send(frame,
+ ev,
+ cli,
+ fname,
+ acl_buf,
+ acl_buf_size);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_setacl_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Stat a file (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_stat_state {
+ struct stat_ex sbuf;
+};
+
+static void cli_posix_stat_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_stat_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct stat_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_posix_stat_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_qpathinfo_send(state, ev, cli, fname,
+ SMB_QUERY_FILE_UNIX_BASIC, 100, 100);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_stat_done, req);
+ return req;
+}
+
+static void cli_posix_stat_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_posix_stat_state *state = tevent_req_data(
+ req, struct cli_posix_stat_state);
+ SMB_STRUCT_STAT *sbuf = &state->sbuf;
+ uint8_t *data;
+ uint32_t num_data = 0;
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &data, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (num_data != 100) {
+ /*
+ * Paranoia, cli_qpathinfo should have guaranteed
+ * this, but you never know...
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ /* total size, in bytes */
+ sbuf->st_ex_size = IVAL2_TO_SMB_BIG_UINT(data, 0);
+
+ /* number of blocks allocated */
+ sbuf->st_ex_blocks = IVAL2_TO_SMB_BIG_UINT(data,8);
+#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
+ sbuf->st_ex_blocks /= STAT_ST_BLOCKSIZE;
+#else
+ /* assume 512 byte blocks */
+ sbuf->st_ex_blocks /= 512;
+#endif
+ /* time of last change */
+ sbuf->st_ex_ctime = interpret_long_date(BVAL(data, 16));
+
+ /* time of last access */
+ sbuf->st_ex_atime = interpret_long_date(BVAL(data, 24));
+
+ /* time of last modification */
+ sbuf->st_ex_mtime = interpret_long_date(BVAL(data, 32));
+
+ sbuf->st_ex_uid = (uid_t) IVAL(data, 40); /* user ID of owner */
+ sbuf->st_ex_gid = (gid_t) IVAL(data, 48); /* group ID of owner */
+ sbuf->st_ex_mode = unix_filetype_from_wire(IVAL(data, 56));
+
+#if defined(HAVE_MAKEDEV)
+ {
+ uint32_t dev_major = IVAL(data,60);
+ uint32_t dev_minor = IVAL(data,68);
+ sbuf->st_ex_rdev = makedev(dev_major, dev_minor);
+ }
+#endif
+ /* inode */
+ sbuf->st_ex_ino = (SMB_INO_T)IVAL2_TO_SMB_BIG_UINT(data, 76);
+
+ /* protection */
+ sbuf->st_ex_mode |= wire_perms_to_unix(IVAL(data, 84));
+
+ /* number of hard links */
+ sbuf->st_ex_nlink = BIG_UINT(data, 92);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_posix_stat_recv(struct tevent_req *req, struct stat_ex *sbuf)
+{
+ struct cli_posix_stat_state *state = tevent_req_data(
+ req, struct cli_posix_stat_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *sbuf = state->sbuf;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_posix_stat(struct cli_state *cli,
+ const char *fname,
+ struct stat_ex *sbuf)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_stat_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_stat_recv(req, sbuf);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Chmod or chown a file internal (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_chown_chmod_internal_state {
+ uint8_t data[100];
+};
+
+static void cli_posix_chown_chmod_internal_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_posix_chown_chmod_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t mode,
+ uint32_t uid,
+ uint32_t gid)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_chown_chmod_internal_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_posix_chown_chmod_internal_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ memset(state->data, 0xff, 40); /* Set all sizes/times to no change. */
+ SIVAL(state->data,40,uid);
+ SIVAL(state->data,48,gid);
+ SIVAL(state->data,84,mode);
+
+ subreq = cli_setpathinfo_send(state, ev, cli, SMB_SET_FILE_UNIX_BASIC,
+ fname, state->data, sizeof(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_chown_chmod_internal_done,
+ req);
+ return req;
+}
+
+static void cli_posix_chown_chmod_internal_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_posix_chown_chmod_internal_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ chmod a file (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_chmod_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_chmod_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_chmod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_chmod_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_posix_chmod_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_posix_chown_chmod_internal_send(
+ state,
+ ev,
+ cli,
+ fname,
+ unix_perms_to_wire(mode),
+ SMB_UID_NO_CHANGE,
+ SMB_GID_NO_CHANGE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_chmod_done, req);
+ return req;
+}
+
+static void cli_posix_chmod_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_chown_chmod_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_chmod_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_chmod(struct cli_state *cli, const char *fname, mode_t mode)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_chmod_send(frame,
+ ev,
+ cli,
+ fname,
+ mode);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_chmod_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ chown a file (UNIX extensions).
+****************************************************************************/
+
+struct cli_posix_chown_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_chown_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_chown_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uid_t uid,
+ gid_t gid)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_chown_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_chown_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_posix_chown_chmod_internal_send(
+ state,
+ ev,
+ cli,
+ fname,
+ SMB_MODE_NO_CHANGE,
+ (uint32_t)uid,
+ (uint32_t)gid);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_chown_done, req);
+ return req;
+}
+
+static void cli_posix_chown_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_chown_chmod_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_chown_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_chown(struct cli_state *cli,
+ const char *fname,
+ uid_t uid,
+ gid_t gid)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_chown_send(frame,
+ ev,
+ cli,
+ fname,
+ uid,
+ gid);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_chown_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb1_posix_mknod_state {
+ uint8_t data[100];
+};
+
+static void cli_smb1_posix_mknod_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_posix_mknod_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode,
+ dev_t dev)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb1_posix_mknod_state *state = NULL;
+ mode_t type = mode & S_IFMT;
+ uint32_t smb_unix_type = 0xFFFFFFFF;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb1_posix_mknod_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ /*
+ * Set all sizes/times/ids to no change.
+ */
+ memset(state->data, 0xff, 56);
+
+ switch (type) {
+ case S_IFREG:
+ smb_unix_type = UNIX_TYPE_FILE;
+ break;
+ case S_IFDIR:
+ smb_unix_type = UNIX_TYPE_DIR;
+ break;
+ case S_IFLNK:
+ smb_unix_type = UNIX_TYPE_SYMLINK;
+ break;
+ case S_IFCHR:
+ smb_unix_type = UNIX_TYPE_CHARDEV;
+ break;
+ case S_IFBLK:
+ smb_unix_type = UNIX_TYPE_BLKDEV;
+ break;
+ case S_IFIFO:
+ smb_unix_type = UNIX_TYPE_FIFO;
+ break;
+ case S_IFSOCK:
+ smb_unix_type = UNIX_TYPE_SOCKET;
+ break;
+ }
+ PUSH_LE_U32(state->data, 56, smb_unix_type);
+
+ if ((type == S_IFCHR) || (type == S_IFBLK)) {
+ PUSH_LE_U64(state->data, 60, unix_dev_major(dev));
+ PUSH_LE_U64(state->data, 68, unix_dev_minor(dev));
+ }
+
+ PUSH_LE_U32(state->data, 84, unix_perms_to_wire(mode));
+
+ subreq = cli_setpathinfo_send(
+ state,
+ ev,
+ cli,
+ SMB_SET_FILE_UNIX_BASIC,
+ fname,
+ state->data,
+ sizeof(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb1_posix_mknod_done, req);
+ return req;
+}
+
+static void cli_smb1_posix_mknod_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb1_posix_mknod_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_mknod_state {
+ uint8_t buf[24];
+};
+
+static void cli_mknod_done1(struct tevent_req *subreq);
+static void cli_mknod_reparse_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_mknod_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode,
+ dev_t dev)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_mknod_state *state = NULL;
+ struct reparse_data_buffer reparse_buf = {
+ .tag = IO_REPARSE_TAG_NFS,
+ };
+ struct nfs_reparse_data_buffer *nfs = &reparse_buf.parsed.nfs;
+ ssize_t buflen;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_mknod_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (cli->requested_posix_capabilities != 0) {
+ subreq = cli_smb1_posix_mknod_send(
+ state, ev, cli, fname, mode, dev);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_mknod_done1, req);
+ return req;
+ }
+
+ /*
+ * Ignored for all but BLK and CHR
+ */
+ nfs->data.dev.major = major(dev);
+ nfs->data.dev.minor = minor(dev);
+
+ switch (mode & S_IFMT) {
+ case S_IFIFO:
+ nfs->type = NFS_SPECFILE_FIFO;
+ break;
+ case S_IFSOCK:
+ nfs->type = NFS_SPECFILE_SOCK;
+ break;
+ case S_IFCHR:
+ nfs->type = NFS_SPECFILE_CHR;
+ break;
+ case S_IFBLK:
+ nfs->type = NFS_SPECFILE_BLK;
+ break;
+ }
+
+ buflen = reparse_data_buffer_marshall(&reparse_buf,
+ state->buf,
+ sizeof(state->buf));
+ if ((buflen == -1) || (buflen > sizeof(state->buf))) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_create_reparse_point_send(state,
+ ev,
+ cli,
+ fname,
+ (DATA_BLOB){
+ .data = state->buf,
+ .length = buflen,
+ });
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_mknod_reparse_done, req);
+ return req;
+}
+
+static void cli_mknod_done1(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb1_posix_mknod_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_mknod_reparse_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_create_reparse_point_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_mknod_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS
+cli_mknod(struct cli_state *cli, const char *fname, mode_t mode, dev_t dev)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_mknod_send(ev, ev, cli, fname, mode, dev);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_mknod_recv(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Rename a file.
+****************************************************************************/
+
+struct cli_smb1_rename_state {
+ uint8_t *data;
+};
+
+static void cli_smb1_rename_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ NTSTATUS status;
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb1_rename_state *state = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb1_rename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Strip a MSDFS path from fname_dst if we were given one.
+ */
+ status = cli_dfs_target_check(state,
+ cli,
+ fname_dst,
+ &fname_dst);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (!push_ucs2_talloc(talloc_tos(), &converted_str, fname_dst,
+ &converted_size_bytes)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* W2K8 insists the dest name is not null
+ terminated. Remove the last 2 zero bytes
+ and reduce the name length. */
+
+ if (converted_size_bytes < 2) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ converted_size_bytes -= 2;
+
+ state->data =
+ talloc_zero_array(state, uint8_t, 12 + converted_size_bytes);
+ if (state->data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (replace) {
+ SCVAL(state->data, 0, 1);
+ }
+
+ SIVAL(state->data, 8, converted_size_bytes);
+ memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+ TALLOC_FREE(converted_str);
+
+ subreq = cli_setpathinfo_send(
+ state, ev, cli, SMB_FILE_RENAME_INFORMATION, fname_src, state->data,
+ talloc_get_size(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, cli_smb1_rename_done, req);
+ return req;
+
+fail:
+ TALLOC_FREE(converted_str);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+}
+
+static void cli_smb1_rename_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb1_rename_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void cli_cifs_rename_done(struct tevent_req *subreq);
+
+struct cli_cifs_rename_state {
+ uint16_t vwv[1];
+};
+
+static struct tevent_req *cli_cifs_rename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_cifs_rename_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_src_cp = NULL;
+ char *fname_dst_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_cifs_rename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (replace) {
+ /*
+ * CIFS doesn't support replace
+ */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ SSVAL(state->vwv+0, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * SMBmv on a DFS share uses DFS names for src and dst.
+ * See smbtorture3: SMB1-DFS-PATHS: test_smb1_mv().
+ */
+
+ fname_src_cp = smb1_dfs_share_path(state, cli, fname_src);
+ if (tevent_req_nomem(fname_src_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_src_cp,
+ strlen(fname_src_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname_src)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ bytes = talloc_realloc(state, bytes, uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * SMBmv on a DFS share uses DFS names for src and dst.
+ * See smbtorture3: SMB1-DFS-PATHS: test_smb1_mv().
+ */
+
+ fname_dst_cp = smb1_dfs_share_path(state, cli, fname_dst);
+ if (tevent_req_nomem(fname_dst_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_dst_cp,
+ strlen(fname_dst_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBmv, additional_flags,
+ additional_flags2,
+ 1, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_cifs_rename_done, req);
+ return req;
+}
+
+static void cli_cifs_rename_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_cifs_rename_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_rename_state {
+ uint8_t dummy;
+};
+
+static void cli_rename_done1(struct tevent_req *subreq);
+static void cli_rename_done_cifs(struct tevent_req *subreq);
+static void cli_rename_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_rename_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_rename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_rename_send(
+ state, ev, cli, fname_src, fname_dst, replace);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_rename_done2, req);
+ return req;
+ }
+
+ if (replace && smbXcli_conn_support_passthrough(cli->conn)) {
+ subreq = cli_smb1_rename_send(
+ state, ev, cli, fname_src, fname_dst, replace);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_rename_done1, req);
+ return req;
+ }
+
+ subreq = cli_cifs_rename_send(
+ state, ev, cli, fname_src,fname_dst, replace);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_rename_done_cifs, req);
+ return req;
+}
+
+static void cli_rename_done1(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb1_rename_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_rename_done_cifs(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_cifs_rename_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_rename_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_rename_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_rename_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_rename(struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_rename_send(frame, ev, cli, fname_src, fname_dst, replace);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_rename_recv(req);
+ cli->raw_status = status; /* cli_smb2_rename_recv doesn't set this */
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ NT Rename a file.
+****************************************************************************/
+
+static void cli_ntrename_internal_done(struct tevent_req *subreq);
+
+struct cli_ntrename_internal_state {
+ uint16_t vwv[4];
+};
+
+static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ uint16_t rename_flag)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_ntrename_internal_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_src_cp = NULL;
+ char *fname_dst_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_ntrename_internal_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(state->vwv+0, 0 ,FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY);
+ SSVAL(state->vwv+1, 0, rename_flag);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBntrename on a DFS share uses DFS names for src and dst.
+ * See smbtorture3: SMB1-DFS-PATHS: test_smb1_ntrename_rename().
+ */
+ fname_src_cp = smb1_dfs_share_path(state, cli, fname_src);
+ if (tevent_req_nomem(fname_src_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_src_cp,
+ strlen(fname_src_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname_src)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ bytes = talloc_realloc(state, bytes, uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * SMBntrename on a DFS share uses DFS names for src and dst.
+ * See smbtorture3: SMB1-DFS-PATHS: test_smb1_ntrename_rename().
+ * and smbtorture3: SMB1-DFS-PATHS: test_smb1_ntrename_hardlink()
+ */
+ fname_dst_cp = smb1_dfs_share_path(state, cli, fname_dst);
+ if (tevent_req_nomem(fname_dst_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_dst_cp,
+ strlen(fname_dst_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBntrename, additional_flags,
+ additional_flags2,
+ 4, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ntrename_internal_done, req);
+ return req;
+}
+
+static void cli_ntrename_internal_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_ntrename_internal_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct tevent_req *cli_ntrename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst)
+{
+ return cli_ntrename_internal_send(mem_ctx,
+ ev,
+ cli,
+ fname_src,
+ fname_dst,
+ RENAME_FLAG_RENAME);
+}
+
+NTSTATUS cli_ntrename_recv(struct tevent_req *req)
+{
+ return cli_ntrename_internal_recv(req);
+}
+
+NTSTATUS cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_ntrename_send(frame, ev, cli, fname_src, fname_dst);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_ntrename_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ NT hardlink a file.
+****************************************************************************/
+
+static struct tevent_req *cli_nt_hardlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst)
+{
+ return cli_ntrename_internal_send(mem_ctx,
+ ev,
+ cli,
+ fname_src,
+ fname_dst,
+ RENAME_FLAG_HARD_LINK);
+}
+
+static NTSTATUS cli_nt_hardlink_recv(struct tevent_req *req)
+{
+ return cli_ntrename_internal_recv(req);
+}
+
+struct cli_smb2_hardlink_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum_src;
+ const char *fname_dst;
+ bool overwrite;
+ NTSTATUS status;
+};
+
+static void cli_smb2_hardlink_opened(struct tevent_req *subreq);
+static void cli_smb2_hardlink_info_set(struct tevent_req *subreq);
+static void cli_smb2_hardlink_closed(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_hardlink_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool overwrite,
+ struct smb2_create_blobs *in_cblobs)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_hardlink_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_hardlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Strip a MSDFS path from fname_dst if we were given one.
+ */
+ status = cli_dfs_target_check(state,
+ cli,
+ fname_dst,
+ &fname_dst);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->ev = ev;
+ state->cli = cli;
+ state->fname_dst = fname_dst;
+ state->overwrite = overwrite;
+
+ subreq = cli_smb2_create_fnum_send(
+ state,
+ ev,
+ cli,
+ fname_src,
+ (struct cli_smb2_create_flags){0},
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_WRITE_ATTRIBUTES,
+ 0, /* file attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_NON_DIRECTORY_FILE, /* no hardlinks on directories */
+ in_cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_hardlink_opened, req);
+ return req;
+}
+
+static void cli_smb2_hardlink_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_hardlink_state *state = tevent_req_data(
+ req, struct cli_smb2_hardlink_state);
+ NTSTATUS status;
+ smb_ucs2_t *ucs2_dst;
+ size_t ucs2_len;
+ DATA_BLOB inbuf;
+ bool ok;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum_src, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ok = push_ucs2_talloc(state, &ucs2_dst, state->fname_dst, &ucs2_len);
+ if (!ok || (ucs2_len < 2)) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ /* Don't 0-terminate the name */
+ ucs2_len -= 2;
+
+ inbuf = data_blob_talloc_zero(state, ucs2_len + 20);
+ if (tevent_req_nomem(inbuf.data, req)) {
+ return;
+ }
+
+ if (state->overwrite) {
+ SCVAL(inbuf.data, 0, 1);
+ }
+ SIVAL(inbuf.data, 16, ucs2_len);
+ memcpy(inbuf.data + 20, ucs2_dst, ucs2_len);
+ TALLOC_FREE(ucs2_dst);
+
+ subreq = cli_smb2_set_info_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fnum_src,
+ 1, /* in_info_type */
+ SMB_FILE_LINK_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf,
+ 0); /* in_additional_info */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_hardlink_info_set, req);
+}
+
+static void cli_smb2_hardlink_info_set(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_hardlink_state *state = tevent_req_data(
+ req, struct cli_smb2_hardlink_state);
+
+ state->status = cli_smb2_set_info_fnum_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ /* ignore error here, we need to close the file */
+
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum_src,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_hardlink_closed, req);
+}
+
+static void cli_smb2_hardlink_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb2_hardlink_recv(struct tevent_req *req)
+{
+ struct cli_smb2_hardlink_state *state = tevent_req_data(
+ req, struct cli_smb2_hardlink_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ return state->status;
+}
+
+struct cli_hardlink_state {
+ uint8_t dummy;
+};
+
+static void cli_hardlink_done(struct tevent_req *subreq);
+static void cli_hardlink_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_hardlink_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_hardlink_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_hardlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_hardlink_send(
+ state, ev, cli, fname_src, fname_dst, false, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_hardlink_done2, req);
+ return req;
+ }
+
+ subreq = cli_nt_hardlink_send(state, ev, cli, fname_src, fname_dst);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_hardlink_done, req);
+ return req;
+}
+
+static void cli_hardlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_nt_hardlink_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_hardlink_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_hardlink_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_hardlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_hardlink(
+ struct cli_state *cli, const char *fname_src, const char *fname_dst)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_hardlink_send(frame, ev, cli, fname_src, fname_dst);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_hardlink_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Delete a file.
+****************************************************************************/
+
+static void cli_unlink_done(struct tevent_req *subreq);
+static void cli_unlink_done2(struct tevent_req *subreq);
+
+struct cli_unlink_state {
+ uint16_t vwv[1];
+};
+
+struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t mayhave_attrs)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_unlink_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_unlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_unlink_send(state, ev, cli, fname, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_unlink_done2, req);
+ return req;
+ }
+
+ if (mayhave_attrs & 0xFFFF0000) {
+ /*
+ * Don't allow attributes greater than
+ * 16-bits for a 16-bit protocol value.
+ */
+ if (tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ SSVAL(state->vwv+0, 0, mayhave_attrs);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBunlink on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBunlink, additional_flags,
+ additional_flags2,
+ 1, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_unlink_done, req);
+ return req;
+}
+
+static void cli_unlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_unlink_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_unlink_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_unlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint32_t mayhave_attrs)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_unlink_send(frame, ev, cli, fname, mayhave_attrs);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_unlink_recv(req);
+ cli->raw_status = status; /* cli_smb2_unlink_recv doesn't set this */
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Create a directory.
+****************************************************************************/
+
+static void cli_mkdir_done(struct tevent_req *subreq);
+static void cli_mkdir_done2(struct tevent_req *subreq);
+
+struct cli_mkdir_state {
+ int dummy;
+};
+
+struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_mkdir_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *dname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_mkdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_mkdir_send(state, ev, cli, dname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_mkdir_done2, req);
+ return req;
+ }
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBmkdir on a DFS share must use DFS names.
+ */
+ dname_cp = smb1_dfs_share_path(state, cli, dname);
+ if (tevent_req_nomem(dname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ dname_cp,
+ strlen(dname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(dname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBmkdir, additional_flags,
+ additional_flags2,
+ 0, NULL, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_mkdir_done, req);
+ return req;
+}
+
+static void cli_mkdir_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_mkdir_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_mkdir_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_mkdir_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_mkdir(struct cli_state *cli, const char *dname)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_mkdir_send(frame, ev, cli, dname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_mkdir_recv(req);
+ cli->raw_status = status; /* cli_smb2_mkdir_recv doesn't set this */
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Remove a directory.
+****************************************************************************/
+
+static void cli_rmdir_done(struct tevent_req *subreq);
+static void cli_rmdir_done2(struct tevent_req *subreq);
+
+struct cli_rmdir_state {
+ int dummy;
+};
+
+struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_rmdir_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *dname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_rmdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_rmdir_send(state, ev, cli, dname, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_rmdir_done2, req);
+ return req;
+ }
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBrmdir on a DFS share must use DFS names.
+ */
+ dname_cp = smb1_dfs_share_path(state, cli, dname);
+ if (tevent_req_nomem(dname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ dname_cp,
+ strlen(dname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(dname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBrmdir, additional_flags,
+ additional_flags2,
+ 0, NULL, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_rmdir_done, req);
+ return req;
+}
+
+static void cli_rmdir_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_rmdir_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_rmdir_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_rmdir_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_rmdir_send(frame, ev, cli, dname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_rmdir_recv(req);
+ cli->raw_status = status; /* cli_smb2_rmdir_recv doesn't set this */
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Set or clear the delete on close flag.
+****************************************************************************/
+
+struct doc_state {
+ uint8_t data[1];
+};
+
+static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq);
+static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool flag)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct doc_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct doc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_delete_on_close_send(state, ev, cli,
+ fnum, flag);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_nt_delete_on_close_smb2_done,
+ req);
+ return req;
+ }
+
+ /* Setup data array. */
+ SCVAL(&state->data[0], 0, flag ? 1 : 0);
+
+ subreq = cli_setfileinfo_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ SMB_SET_FILE_DISPOSITION_INFO,
+ state->data,
+ sizeof(state->data));
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_nt_delete_on_close_smb1_done,
+ req);
+ return req;
+}
+
+static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setfileinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_delete_on_close_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_nt_delete_on_close_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_nt_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_nt_delete_on_close_send(frame,
+ ev,
+ cli,
+ fnum,
+ flag);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_nt_delete_on_close_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_ntcreate1_state {
+ uint16_t vwv[24];
+ uint16_t fnum;
+ struct smb_create_returns cr;
+ struct tevent_req *subreq;
+};
+
+static void cli_ntcreate1_done(struct tevent_req *subreq);
+static bool cli_ntcreate1_cancel(struct tevent_req *req);
+
+static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint32_t ImpersonationLevel,
+ uint8_t SecurityFlags)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_ntcreate1_state *state;
+ uint16_t *vwv;
+ uint8_t *bytes;
+ size_t converted_len;
+ uint16_t additional_flags2 = 0;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate1_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ vwv = state->vwv;
+
+ SCVAL(vwv+0, 0, 0xFF);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SCVAL(vwv+2, 0, 0);
+
+ if (cli->use_oplocks) {
+ CreatFlags |= (REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK);
+ }
+ SIVAL(vwv+3, 1, CreatFlags);
+ SIVAL(vwv+5, 1, 0x0); /* RootDirectoryFid */
+ SIVAL(vwv+7, 1, DesiredAccess);
+ SIVAL(vwv+9, 1, 0x0); /* AllocationSize */
+ SIVAL(vwv+11, 1, 0x0); /* AllocationSize */
+ SIVAL(vwv+13, 1, FileAttributes);
+ SIVAL(vwv+15, 1, ShareAccess);
+ SIVAL(vwv+17, 1, CreateDisposition);
+ SIVAL(vwv+19, 1, CreateOptions |
+ (cli->backup_intent ? FILE_OPEN_FOR_BACKUP_INTENT : 0));
+ SIVAL(vwv+21, 1, ImpersonationLevel);
+ SCVAL(vwv+23, 1, SecurityFlags);
+
+ bytes = talloc_array(state, uint8_t, 0);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBntcreateX on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ &converted_len);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ /* sigh. this copes with broken netapp filer behaviour */
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SSVAL(vwv+2, 1, converted_len);
+
+ subreq = cli_smb_send(state, ev, cli, SMBntcreateX, 0,
+ additional_flags2, 24, vwv,
+ talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ntcreate1_done, req);
+
+ state->subreq = subreq;
+ tevent_req_set_cancel_fn(req, cli_ntcreate1_cancel);
+
+ return req;
+}
+
+static void cli_ntcreate1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_ntcreate1_state *state = tevent_req_data(
+ req, struct cli_ntcreate1_state);
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 34, &wct, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->cr.oplock_level = CVAL(vwv+2, 0);
+ state->fnum = SVAL(vwv+2, 1);
+ state->cr.create_action = IVAL(vwv+3, 1);
+ state->cr.creation_time = BVAL(vwv+5, 1);
+ state->cr.last_access_time = BVAL(vwv+9, 1);
+ state->cr.last_write_time = BVAL(vwv+13, 1);
+ state->cr.change_time = BVAL(vwv+17, 1);
+ state->cr.file_attributes = IVAL(vwv+21, 1);
+ state->cr.allocation_size = BVAL(vwv+23, 1);
+ state->cr.end_of_file = BVAL(vwv+27, 1);
+
+ tevent_req_done(req);
+}
+
+static bool cli_ntcreate1_cancel(struct tevent_req *req)
+{
+ struct cli_ntcreate1_state *state = tevent_req_data(
+ req, struct cli_ntcreate1_state);
+ return tevent_req_cancel(state->subreq);
+}
+
+static NTSTATUS cli_ntcreate1_recv(struct tevent_req *req,
+ uint16_t *pfnum,
+ struct smb_create_returns *cr)
+{
+ struct cli_ntcreate1_state *state = tevent_req_data(
+ req, struct cli_ntcreate1_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ return NT_STATUS_OK;
+}
+
+struct cli_ntcreate_state {
+ struct smb_create_returns cr;
+ uint16_t fnum;
+ struct tevent_req *subreq;
+};
+
+static void cli_ntcreate_done_nt1(struct tevent_req *subreq);
+static void cli_ntcreate_done_smb2(struct tevent_req *subreq);
+static bool cli_ntcreate_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t create_flags,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ uint32_t impersonation_level,
+ uint8_t security_flags)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_ntcreate_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ struct cli_smb2_create_flags cflags = {0};
+
+ if (cli->use_oplocks) {
+ create_flags |= REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK;
+ }
+
+ cflags = (struct cli_smb2_create_flags) {
+ .batch_oplock = (create_flags & REQUEST_BATCH_OPLOCK),
+ .exclusive_oplock = (create_flags & REQUEST_OPLOCK),
+ };
+
+ subreq = cli_smb2_create_fnum_send(
+ state,
+ ev,
+ cli,
+ fname,
+ cflags,
+ impersonation_level,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ntcreate_done_smb2, req);
+ } else {
+ subreq = cli_ntcreate1_send(
+ state, ev, cli, fname, create_flags, desired_access,
+ file_attributes, share_access, create_disposition,
+ create_options, impersonation_level, security_flags);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ntcreate_done_nt1, req);
+ }
+
+ state->subreq = subreq;
+ tevent_req_set_cancel_fn(req, cli_ntcreate_cancel);
+
+ return req;
+}
+
+static void cli_ntcreate_done_nt1(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_ntcreate_state *state = tevent_req_data(
+ req, struct cli_ntcreate_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate1_recv(subreq, &state->fnum, &state->cr);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_ntcreate_done_smb2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_ntcreate_state *state = tevent_req_data(
+ req, struct cli_ntcreate_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq,
+ &state->fnum,
+ &state->cr,
+ NULL,
+ NULL,
+ NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static bool cli_ntcreate_cancel(struct tevent_req *req)
+{
+ struct cli_ntcreate_state *state = tevent_req_data(
+ req, struct cli_ntcreate_state);
+ return tevent_req_cancel(state->subreq);
+}
+
+NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *fnum,
+ struct smb_create_returns *cr)
+{
+ struct cli_ntcreate_state *state = tevent_req_data(
+ req, struct cli_ntcreate_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (fnum != NULL) {
+ *fnum = state->fnum;
+ }
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_ntcreate(struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ uint16_t *pfid,
+ struct smb_create_returns *cr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ uint32_t ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_ntcreate_send(frame, ev, cli, fname, CreatFlags,
+ DesiredAccess, FileAttributes, ShareAccess,
+ CreateDisposition, CreateOptions,
+ ImpersonationLevel, SecurityFlags);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_ntcreate_recv(req, pfid, cr);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_nttrans_create_state {
+ uint16_t fnum;
+ struct smb_create_returns cr;
+};
+
+static void cli_nttrans_create_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ struct security_descriptor *secdesc,
+ struct ea_struct *eas,
+ int num_eas)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_nttrans_create_state *state;
+ uint8_t *param;
+ uint8_t *secdesc_buf;
+ size_t secdesc_len;
+ NTSTATUS status;
+ size_t converted_len;
+ uint16_t additional_flags2 = 0;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state, struct cli_nttrans_create_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (secdesc != NULL) {
+ status = marshall_sec_desc(talloc_tos(), secdesc,
+ &secdesc_buf, &secdesc_len);
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("marshall_sec_desc failed: %s\n",
+ nt_errstr(status)));
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ secdesc_buf = NULL;
+ secdesc_len = 0;
+ }
+
+ if (num_eas != 0) {
+ /*
+ * TODO ;-)
+ */
+ tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+ return tevent_req_post(req, ev);
+ }
+
+ param = talloc_array(state, uint8_t, 53);
+ if (tevent_req_nomem(param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * SMBntcreateX on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp),
+ &converted_len);
+ if (tevent_req_nomem(param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ SIVAL(param, 0, CreatFlags);
+ SIVAL(param, 4, 0x0); /* RootDirectoryFid */
+ SIVAL(param, 8, DesiredAccess);
+ SIVAL(param, 12, 0x0); /* AllocationSize */
+ SIVAL(param, 16, 0x0); /* AllocationSize */
+ SIVAL(param, 20, FileAttributes);
+ SIVAL(param, 24, ShareAccess);
+ SIVAL(param, 28, CreateDisposition);
+ SIVAL(param, 32, CreateOptions |
+ (cli->backup_intent ? FILE_OPEN_FOR_BACKUP_INTENT : 0));
+ SIVAL(param, 36, secdesc_len);
+ SIVAL(param, 40, 0); /* EA length*/
+ SIVAL(param, 44, converted_len);
+ SIVAL(param, 48, 0x02); /* ImpersonationLevel */
+ SCVAL(param, 52, SecurityFlags);
+
+ subreq = cli_trans_send(state, ev, cli,
+ additional_flags2, /* additional_flags2 */
+ SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_CREATE, 0,
+ NULL, 0, 0, /* setup */
+ param, talloc_get_size(param), 128, /* param */
+ secdesc_buf, secdesc_len, 0); /* data */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_nttrans_create_done, req);
+ return req;
+}
+
+static void cli_nttrans_create_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_nttrans_create_state *state = tevent_req_data(
+ req, struct cli_nttrans_create_state);
+ uint8_t *param;
+ uint32_t num_param;
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, talloc_tos(), NULL,
+ NULL, 0, NULL, /* rsetup */
+ &param, 69, &num_param,
+ NULL, 0, NULL);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->cr.oplock_level = CVAL(param, 0);
+ state->fnum = SVAL(param, 2);
+ state->cr.create_action = IVAL(param, 4);
+ state->cr.creation_time = BVAL(param, 12);
+ state->cr.last_access_time = BVAL(param, 20);
+ state->cr.last_write_time = BVAL(param, 28);
+ state->cr.change_time = BVAL(param, 36);
+ state->cr.file_attributes = IVAL(param, 44);
+ state->cr.allocation_size = BVAL(param, 48);
+ state->cr.end_of_file = BVAL(param, 56);
+
+ TALLOC_FREE(param);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_nttrans_create_recv(struct tevent_req *req,
+ uint16_t *fnum,
+ struct smb_create_returns *cr)
+{
+ struct cli_nttrans_create_state *state = tevent_req_data(
+ req, struct cli_nttrans_create_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *fnum = state->fnum;
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_nttrans_create(struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ struct security_descriptor *secdesc,
+ struct ea_struct *eas,
+ int num_eas,
+ uint16_t *pfid,
+ struct smb_create_returns *cr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_nttrans_create_send(frame, ev, cli, fname, CreatFlags,
+ DesiredAccess, FileAttributes,
+ ShareAccess, CreateDisposition,
+ CreateOptions, SecurityFlags,
+ secdesc, eas, num_eas);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_nttrans_create_recv(req, pfid, cr);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Open a file
+ WARNING: if you open with O_WRONLY then getattrE won't work!
+****************************************************************************/
+
+struct cli_openx_state {
+ const char *fname;
+ uint16_t vwv[15];
+ uint16_t fnum;
+ struct iovec bytes;
+};
+
+static void cli_openx_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ int flags, int share_mode,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_openx_state *state;
+ unsigned openfn;
+ unsigned accessmode;
+ uint8_t additional_flags;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_openx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ openfn = 0;
+ if (flags & O_CREAT) {
+ openfn |= (1<<4);
+ }
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC)
+ openfn |= (1<<1);
+ else
+ openfn |= (1<<0);
+ }
+
+ accessmode = (share_mode<<4);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ accessmode |= 2;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ accessmode |= 1;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ accessmode |= (1<<14);
+ }
+#endif /* O_SYNC */
+
+ if (share_mode == DENY_FCB) {
+ accessmode = 0xFF;
+ }
+
+ SCVAL(state->vwv + 0, 0, 0xFF);
+ SCVAL(state->vwv + 0, 1, 0);
+ SSVAL(state->vwv + 1, 0, 0);
+ SSVAL(state->vwv + 2, 0, 0); /* no additional info */
+ SSVAL(state->vwv + 3, 0, accessmode);
+ SSVAL(state->vwv + 4, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ SSVAL(state->vwv + 5, 0, 0);
+ SIVAL(state->vwv + 6, 0, 0);
+ SSVAL(state->vwv + 8, 0, openfn);
+ SIVAL(state->vwv + 9, 0, 0);
+ SIVAL(state->vwv + 11, 0, 0);
+ SIVAL(state->vwv + 13, 0, 0);
+
+ additional_flags = 0;
+
+ if (cli->use_oplocks) {
+ /* if using oplocks then ask for a batch oplock via
+ core and extended methods */
+ additional_flags =
+ FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK;
+ SSVAL(state->vwv+2, 0, SVAL(state->vwv+2, 0) | 6);
+ }
+
+ bytes = talloc_array(state, uint8_t, 0);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBopenX on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBopenX, additional_flags,
+ additional_flags2, 15, state->vwv, 1, &state->bytes);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cli_openx_done, req);
+ *psmbreq = subreq;
+ return req;
+}
+
+struct tevent_req *cli_openx_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ int flags, int share_mode)
+{
+ struct tevent_req *req, *subreq;
+ NTSTATUS status;
+
+ req = cli_openx_create(mem_ctx, ev, cli, fname, flags, share_mode,
+ &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void cli_openx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_openx_state *state = tevent_req_data(
+ req, struct cli_openx_state);
+ uint8_t wct;
+ uint16_t *vwv;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 3, &wct, &vwv, NULL,
+ NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->fnum = SVAL(vwv+2, 0);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_openx_recv(struct tevent_req *req, uint16_t *pfnum)
+{
+ struct cli_openx_state *state = tevent_req_data(
+ req, struct cli_openx_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_openx(struct cli_state *cli, const char *fname, int flags,
+ int share_mode, uint16_t *pfnum)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = cli_openx_send(frame, ev, cli, fname, flags, share_mode);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_openx_recv(req, pfnum);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+/****************************************************************************
+ Synchronous wrapper function that does an NtCreateX open by preference
+ and falls back to openX if this fails.
+****************************************************************************/
+
+NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags,
+ int share_mode_in, uint16_t *pfnum)
+{
+ NTSTATUS status;
+ unsigned int openfn = 0;
+ unsigned int dos_deny = 0;
+ uint32_t access_mask, share_mode, create_disposition, create_options;
+ struct smb_create_returns cr = {0};
+
+ /* Do the initial mapping into OpenX parameters. */
+ if (flags & O_CREAT) {
+ openfn |= (1<<4);
+ }
+ if (!(flags & O_EXCL)) {
+ if (flags & O_TRUNC)
+ openfn |= (1<<1);
+ else
+ openfn |= (1<<0);
+ }
+
+ dos_deny = (share_mode_in<<4);
+
+ if ((flags & O_ACCMODE) == O_RDWR) {
+ dos_deny |= 2;
+ } else if ((flags & O_ACCMODE) == O_WRONLY) {
+ dos_deny |= 1;
+ }
+
+#if defined(O_SYNC)
+ if ((flags & O_SYNC) == O_SYNC) {
+ dos_deny |= (1<<14);
+ }
+#endif /* O_SYNC */
+
+ if (share_mode_in == DENY_FCB) {
+ dos_deny = 0xFF;
+ }
+
+ if (!map_open_params_to_ntcreate(fname, dos_deny,
+ openfn, &access_mask,
+ &share_mode, &create_disposition,
+ &create_options, NULL)) {
+ goto try_openx;
+ }
+
+ status = cli_ntcreate(cli,
+ fname,
+ 0,
+ access_mask,
+ 0,
+ share_mode,
+ create_disposition,
+ create_options,
+ 0,
+ pfnum,
+ &cr);
+
+ /* Try and cope will all variants of "we don't do this call"
+ and fall back to openX. */
+
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_INFO_CLASS) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_PROCEDURE_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_REQUEST) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_STATE) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_CTL_FILE_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_UNSUCCESSFUL)) {
+ goto try_openx;
+ }
+
+ if (NT_STATUS_IS_OK(status) &&
+ (create_options & FILE_NON_DIRECTORY_FILE) &&
+ (cr.file_attributes & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /*
+ * Some (broken) servers return a valid handle
+ * for directories even if FILE_NON_DIRECTORY_FILE
+ * is set. Just close the handle and set the
+ * error explicitly to NT_STATUS_FILE_IS_A_DIRECTORY.
+ */
+ status = cli_close(cli, *pfnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = NT_STATUS_FILE_IS_A_DIRECTORY;
+ /* Set this so libsmbclient can retrieve it. */
+ cli->raw_status = status;
+ }
+
+ return status;
+
+ try_openx:
+
+ return cli_openx(cli, fname, flags, share_mode_in, pfnum);
+}
+
+/****************************************************************************
+ Close a file.
+****************************************************************************/
+
+struct cli_smb1_close_state {
+ uint16_t vwv[3];
+};
+
+static void cli_smb1_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb1_close_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ struct tevent_req **psubreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb1_close_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb1_close_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(state->vwv+0, 0, fnum);
+ SIVALS(state->vwv+1, 0, -1);
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBclose, 0, 0,
+ 3, state->vwv, 0, NULL);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cli_smb1_close_done, req);
+ *psubreq = subreq;
+ return req;
+}
+
+static void cli_smb1_close_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+struct cli_close_state {
+ int dummy;
+};
+
+static void cli_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t flags)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_close_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_close_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_close_fnum_send(state, ev, cli, fnum, flags);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ struct tevent_req *ch_req = NULL;
+ subreq = cli_smb1_close_create(state, ev, cli, fnum, &ch_req);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ status = smb1cli_req_chain_submit(&ch_req, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ tevent_req_set_callback(subreq, cli_close_done, req);
+ return req;
+}
+
+static void cli_close_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status = NT_STATUS_OK;
+ bool err = tevent_req_is_nterror(subreq, &status);
+
+ TALLOC_FREE(subreq);
+ if (err) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_close_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_close_send(frame, ev, cli, fnum, 0);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_close_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Truncate a file to a specified size
+****************************************************************************/
+
+struct ftrunc_state {
+ uint8_t data[8];
+};
+
+static void cli_ftruncate_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setfileinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+struct tevent_req *cli_ftruncate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t size)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ftrunc_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct ftrunc_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup data array. */
+ SBVAL(state->data, 0, size);
+
+ subreq = cli_setfileinfo_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ SMB_SET_FILE_END_OF_FILE_INFO,
+ state->data,
+ sizeof(state->data));
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ftruncate_done, req);
+ return req;
+}
+
+NTSTATUS cli_ftruncate_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_ftruncate(struct cli_state *cli, uint16_t fnum, uint64_t size)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_ftruncate(cli, fnum, size);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_ftruncate_send(frame,
+ ev,
+ cli,
+ fnum,
+ size);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_ftruncate_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static uint8_t *cli_lockingx_put_locks(
+ uint8_t *buf,
+ bool large,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks)
+{
+ uint16_t i;
+
+ for (i=0; i<num_locks; i++) {
+ const struct smb1_lock_element *e = &locks[i];
+ if (large) {
+ SSVAL(buf, 0, e->pid);
+ SSVAL(buf, 2, 0);
+ SOFF_T_R(buf, 4, e->offset);
+ SOFF_T_R(buf, 12, e->length);
+ buf += 20;
+ } else {
+ SSVAL(buf, 0, e->pid);
+ SIVAL(buf, 2, e->offset);
+ SIVAL(buf, 6, e->length);
+ buf += 10;
+ }
+ }
+ return buf;
+}
+
+struct cli_lockingx_state {
+ uint16_t vwv[8];
+ struct iovec bytes;
+ struct tevent_req *subreq;
+};
+
+static void cli_lockingx_done(struct tevent_req *subreq);
+static bool cli_lockingx_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_lockingx_create(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_lockingx_state *state = NULL;
+ uint16_t *vwv;
+ uint8_t *p;
+ const bool large = (typeoflock & LOCKING_ANDX_LARGE_FILES);
+ const size_t element_len = large ? 20 : 10;
+
+ /* uint16->size_t, no overflow */
+ const size_t num_elements = (size_t)num_locks + (size_t)num_unlocks;
+
+ /* at most 20*2*65535 = 2621400, no overflow */
+ const size_t num_bytes = num_elements * element_len;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_lockingx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ vwv = state->vwv;
+
+ SCVAL(vwv + 0, 0, 0xFF);
+ SCVAL(vwv + 0, 1, 0);
+ SSVAL(vwv + 1, 0, 0);
+ SSVAL(vwv + 2, 0, fnum);
+ SCVAL(vwv + 3, 0, typeoflock);
+ SCVAL(vwv + 3, 1, newoplocklevel);
+ SIVALS(vwv + 4, 0, timeout);
+ SSVAL(vwv + 6, 0, num_unlocks);
+ SSVAL(vwv + 7, 0, num_locks);
+
+ state->bytes.iov_len = num_bytes;
+ state->bytes.iov_base = talloc_array(state, uint8_t, num_bytes);
+ if (tevent_req_nomem(state->bytes.iov_base, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ p = cli_lockingx_put_locks(
+ state->bytes.iov_base, large, num_unlocks, unlocks);
+ cli_lockingx_put_locks(p, large, num_locks, locks);
+
+ subreq = cli_smb_req_create(
+ state, ev, cli, SMBlockingX, 0, 0, 8, vwv, 1, &state->bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_lockingx_done, req);
+ *psmbreq = subreq;
+ return req;
+}
+
+struct tevent_req *cli_lockingx_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_lockingx_state *state = NULL;
+ NTSTATUS status;
+
+ req = cli_lockingx_create(
+ mem_ctx,
+ ev,
+ cli,
+ fnum,
+ typeoflock,
+ newoplocklevel,
+ timeout,
+ num_unlocks,
+ unlocks,
+ num_locks,
+ locks,
+ &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+ state = tevent_req_data(req, struct cli_lockingx_state);
+ state->subreq = subreq;
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_cancel_fn(req, cli_lockingx_cancel);
+ return req;
+}
+
+static void cli_lockingx_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static bool cli_lockingx_cancel(struct tevent_req *req)
+{
+ struct cli_lockingx_state *state = tevent_req_data(
+ req, struct cli_lockingx_state);
+ if (state->subreq == NULL) {
+ return false;
+ }
+ return tevent_req_cancel(state->subreq);
+}
+
+NTSTATUS cli_lockingx_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_lockingx(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ unsigned int set_timeout = 0;
+ unsigned int saved_timeout = 0;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ if (timeout != 0) {
+ if (timeout == -1) {
+ set_timeout = 0x7FFFFFFF;
+ } else {
+ set_timeout = timeout + 2*1000;
+ }
+ saved_timeout = cli_set_timeout(cli, set_timeout);
+ }
+
+ req = cli_lockingx_send(
+ frame,
+ ev,
+ cli,
+ fnum,
+ typeoflock,
+ newoplocklevel,
+ timeout,
+ num_unlocks,
+ unlocks,
+ num_locks,
+ locks);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_lockingx_recv(req);
+
+ if (saved_timeout != 0) {
+ cli_set_timeout(cli, saved_timeout);
+ }
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ send a lock with a specified locktype
+ this is used for testing LOCKING_ANDX_CANCEL_LOCK
+****************************************************************************/
+
+NTSTATUS cli_locktype(struct cli_state *cli, uint16_t fnum,
+ uint32_t offset, uint32_t len,
+ int timeout, unsigned char locktype)
+{
+ struct smb1_lock_element lck = {
+ .pid = cli_getpid(cli),
+ .offset = offset,
+ .length = len,
+ };
+ NTSTATUS status;
+
+ status = cli_lockingx(
+ cli, /* cli */
+ fnum, /* fnum */
+ locktype, /* typeoflock */
+ 0, /* newoplocklevel */
+ timeout, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 1, /* num_locks */
+ &lck); /* locks */
+ return status;
+}
+
+/****************************************************************************
+ Lock a file.
+ note that timeout is in units of 2 milliseconds
+****************************************************************************/
+
+NTSTATUS cli_lock32(struct cli_state *cli, uint16_t fnum,
+ uint32_t offset, uint32_t len, int timeout,
+ enum brl_type lock_type)
+{
+ NTSTATUS status;
+
+ status = cli_locktype(cli, fnum, offset, len, timeout,
+ (lock_type == READ_LOCK? 1 : 0));
+ return status;
+}
+
+/****************************************************************************
+ Unlock a file.
+****************************************************************************/
+
+struct cli_unlock_state {
+ struct smb1_lock_element lck;
+};
+
+static void cli_unlock_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_unlock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len)
+
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_unlock_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_unlock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->lck = (struct smb1_lock_element) {
+ .pid = cli_getpid(cli),
+ .offset = offset,
+ .length = len,
+ };
+
+ subreq = cli_lockingx_send(
+ state, /* mem_ctx */
+ ev, /* tevent_context */
+ cli, /* cli */
+ fnum, /* fnum */
+ 0, /* typeoflock */
+ 0, /* newoplocklevel */
+ 0, /* timeout */
+ 1, /* num_unlocks */
+ &state->lck, /* unlocks */
+ 0, /* num_locks */
+ NULL); /* locks */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_unlock_done, req);
+ return req;
+}
+
+static void cli_unlock_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_lockingx_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_unlock_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_unlock(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t offset,
+ uint32_t len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_unlock_send(frame, ev, cli,
+ fnum, offset, len);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_unlock_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Get/unlock a POSIX lock on a file - internal function.
+****************************************************************************/
+
+struct posix_lock_state {
+ uint16_t setup;
+ uint8_t param[4];
+ uint8_t data[POSIX_LOCK_DATA_SIZE];
+};
+
+static void cli_posix_unlock_internal_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
+ NULL, 0, NULL, NULL, 0, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static struct tevent_req *cli_posix_lock_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len,
+ bool wait_lock,
+ enum brl_type lock_type)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct posix_lock_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct posix_lock_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup setup word. */
+ SSVAL(&state->setup, 0, TRANSACT2_SETFILEINFO);
+
+ /* Setup param array. */
+ SSVAL(&state->param, 0, fnum);
+ SSVAL(&state->param, 2, SMB_SET_POSIX_LOCK);
+
+ /* Setup data array. */
+ switch (lock_type) {
+ case READ_LOCK:
+ SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET,
+ POSIX_LOCK_TYPE_READ);
+ break;
+ case WRITE_LOCK:
+ SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET,
+ POSIX_LOCK_TYPE_WRITE);
+ break;
+ case UNLOCK_LOCK:
+ SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET,
+ POSIX_LOCK_TYPE_UNLOCK);
+ break;
+ default:
+ return NULL;
+ }
+
+ if (wait_lock) {
+ SSVAL(&state->data, POSIX_LOCK_FLAGS_OFFSET,
+ POSIX_LOCK_FLAG_WAIT);
+ } else {
+ SSVAL(state->data, POSIX_LOCK_FLAGS_OFFSET,
+ POSIX_LOCK_FLAG_NOWAIT);
+ }
+
+ SIVAL(&state->data, POSIX_LOCK_PID_OFFSET, cli_getpid(cli));
+ SOFF_T(&state->data, POSIX_LOCK_START_OFFSET, offset);
+ SOFF_T(&state->data, POSIX_LOCK_LEN_OFFSET, len);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ &state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ 4, /* num param. */
+ 2, /* max returned param. */
+ state->data, /* data. */
+ POSIX_LOCK_DATA_SIZE, /* num data. */
+ 0); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_unlock_internal_done, req);
+ return req;
+}
+
+/****************************************************************************
+ POSIX Lock a file.
+****************************************************************************/
+
+struct tevent_req *cli_posix_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len,
+ bool wait_lock,
+ enum brl_type lock_type)
+{
+ return cli_posix_lock_internal_send(mem_ctx, ev, cli, fnum, offset, len,
+ wait_lock, lock_type);
+}
+
+NTSTATUS cli_posix_lock_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_lock(struct cli_state *cli, uint16_t fnum,
+ uint64_t offset, uint64_t len,
+ bool wait_lock, enum brl_type lock_type)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (lock_type != READ_LOCK && lock_type != WRITE_LOCK) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_lock_send(frame,
+ ev,
+ cli,
+ fnum,
+ offset,
+ len,
+ wait_lock,
+ lock_type);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_lock_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ POSIX Unlock a file.
+****************************************************************************/
+
+struct tevent_req *cli_posix_unlock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len)
+{
+ return cli_posix_lock_internal_send(mem_ctx, ev, cli, fnum, offset, len,
+ false, UNLOCK_LOCK);
+}
+
+NTSTATUS cli_posix_unlock_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_unlock(struct cli_state *cli, uint16_t fnum, uint64_t offset, uint64_t len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_unlock_send(frame,
+ ev,
+ cli,
+ fnum,
+ offset,
+ len);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_unlock_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Do a SMBgetattrE call.
+****************************************************************************/
+
+static void cli_getattrE_done(struct tevent_req *subreq);
+
+struct cli_getattrE_state {
+ uint16_t vwv[1];
+ int zone_offset;
+ uint32_t attr;
+ off_t size;
+ time_t change_time;
+ time_t access_time;
+ time_t write_time;
+};
+
+struct tevent_req *cli_getattrE_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_getattrE_state *state = NULL;
+ uint8_t additional_flags = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_getattrE_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->zone_offset = smb1cli_conn_server_time_zone(cli->conn);
+ SSVAL(state->vwv+0,0,fnum);
+
+ subreq = cli_smb_send(state, ev, cli, SMBgetattrE, additional_flags, 0,
+ 1, state->vwv, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_getattrE_done, req);
+ return req;
+}
+
+static void cli_getattrE_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_getattrE_state *state = tevent_req_data(
+ req, struct cli_getattrE_state);
+ uint8_t wct;
+ uint16_t *vwv = NULL;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 11, &wct, &vwv,
+ NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->size = (off_t)IVAL(vwv+6,0);
+ state->attr = SVAL(vwv+10,0);
+ state->change_time = make_unix_date2(vwv+0, state->zone_offset);
+ state->access_time = make_unix_date2(vwv+2, state->zone_offset);
+ state->write_time = make_unix_date2(vwv+4, state->zone_offset);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_getattrE_recv(struct tevent_req *req,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time)
+{
+ struct cli_getattrE_state *state = tevent_req_data(
+ req, struct cli_getattrE_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pattr) {
+ *pattr = state->attr;
+ }
+ if (size) {
+ *size = state->size;
+ }
+ if (change_time) {
+ *change_time = state->change_time;
+ }
+ if (access_time) {
+ *access_time = state->access_time;
+ }
+ if (write_time) {
+ *write_time = state->write_time;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a SMBgetatr call
+****************************************************************************/
+
+static void cli_getatr_done(struct tevent_req *subreq);
+
+struct cli_getatr_state {
+ int zone_offset;
+ uint32_t attr;
+ off_t size;
+ time_t write_time;
+};
+
+struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_getatr_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_getatr_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->zone_offset = smb1cli_conn_server_time_zone(cli->conn);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBgetatr on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBgetatr, additional_flags,
+ additional_flags2,
+ 0, NULL, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_getatr_done, req);
+ return req;
+}
+
+static void cli_getatr_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_getatr_state *state = tevent_req_data(
+ req, struct cli_getatr_state);
+ uint8_t wct;
+ uint16_t *vwv = NULL;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 4, &wct, &vwv, NULL,
+ NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->attr = SVAL(vwv+0,0);
+ state->size = (off_t)IVAL(vwv+3,0);
+ state->write_time = make_unix_date3(vwv+1, state->zone_offset);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_getatr_recv(struct tevent_req *req,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *write_time)
+{
+ struct cli_getatr_state *state = tevent_req_data(
+ req, struct cli_getatr_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pattr) {
+ *pattr = state->attr;
+ }
+ if (size) {
+ *size = state->size;
+ }
+ if (write_time) {
+ *write_time = state->write_time;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_getatr(struct cli_state *cli,
+ const char *fname,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *write_time)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ struct stat_ex sbuf = {
+ .st_ex_nlink = 0,
+ };
+ uint32_t attr;
+
+ status = cli_smb2_qpathinfo_basic(cli, fname, &sbuf, &attr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pattr != NULL) {
+ *pattr = attr;
+ }
+ if (size != NULL) {
+ *size = sbuf.st_ex_size;
+ }
+ if (write_time != NULL) {
+ *write_time = sbuf.st_ex_mtime.tv_sec;
+ }
+ return NT_STATUS_OK;
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_getatr_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_getatr_recv(req,
+ pattr,
+ size,
+ write_time);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Do a SMBsetattrE call.
+****************************************************************************/
+
+static void cli_setattrE_done(struct tevent_req *subreq);
+
+struct cli_setattrE_state {
+ uint16_t vwv[7];
+};
+
+struct tevent_req *cli_setattrE_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_setattrE_state *state = NULL;
+ uint8_t additional_flags = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_setattrE_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(state->vwv+0, 0, fnum);
+ push_dos_date2((uint8_t *)&state->vwv[1], 0, change_time,
+ smb1cli_conn_server_time_zone(cli->conn));
+ push_dos_date2((uint8_t *)&state->vwv[3], 0, access_time,
+ smb1cli_conn_server_time_zone(cli->conn));
+ push_dos_date2((uint8_t *)&state->vwv[5], 0, write_time,
+ smb1cli_conn_server_time_zone(cli->conn));
+
+ subreq = cli_smb_send(state, ev, cli, SMBsetattrE, additional_flags, 0,
+ 7, state->vwv, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_setattrE_done, req);
+ return req;
+}
+
+static void cli_setattrE_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_setattrE_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_setattrE(struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_setattrE(cli,
+ fnum,
+ change_time,
+ access_time,
+ write_time);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_setattrE_send(frame, ev,
+ cli,
+ fnum,
+ change_time,
+ access_time,
+ write_time);
+
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_setattrE_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Do a SMBsetatr call.
+****************************************************************************/
+
+static void cli_setatr_done(struct tevent_req *subreq);
+
+struct cli_setatr_state {
+ uint16_t vwv[8];
+};
+
+struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t attr,
+ time_t mtime)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_setatr_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_setatr_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (attr & 0xFFFF0000) {
+ /*
+ * Don't allow attributes greater than
+ * 16-bits for a 16-bit protocol value.
+ */
+ if (tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ SSVAL(state->vwv+0, 0, attr);
+ push_dos_date3((uint8_t *)&state->vwv[1], 0, mtime, smb1cli_conn_server_time_zone(cli->conn));
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBsetatr on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = talloc_realloc(state, bytes, uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "",
+ 1, NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBsetatr, additional_flags,
+ additional_flags2,
+ 8, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_setatr_done, req);
+ return req;
+}
+
+static void cli_setatr_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_setatr_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_setatr(struct cli_state *cli,
+ const char *fname,
+ uint32_t attr,
+ time_t mtime)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_setatr(cli,
+ fname,
+ attr,
+ mtime);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_setatr_send(frame, ev, cli, fname, attr, mtime);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_setatr_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Check for existence of a dir.
+****************************************************************************/
+
+static void cli_chkpath_done(struct tevent_req *subreq);
+static void cli_chkpath_opened(struct tevent_req *subreq);
+static void cli_chkpath_closed(struct tevent_req *subreq);
+
+struct cli_chkpath_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+};
+
+struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_chkpath_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_chkpath_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) {
+ subreq = cli_ntcreate_send(
+ state, /* mem_ctx */
+ state->ev, /* ev */
+ state->cli, /* cli */
+ fname, /* fname */
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* CreateDisposition */
+ FILE_DIRECTORY_FILE, /* CreateOptions */
+ SMB2_IMPERSONATION_IMPERSONATION,
+ 0); /* SecurityFlags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_chkpath_opened, req);
+ return req;
+ }
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBcheckpath on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBcheckpath, additional_flags,
+ additional_flags2,
+ 0, NULL, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_chkpath_done, req);
+ return req;
+}
+
+static void cli_chkpath_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(
+ subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_chkpath_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_chkpath_state *state = tevent_req_data(
+ req, struct cli_chkpath_state);
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_ntcreate_recv(subreq, &fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_close_send(state, state->ev, state->cli, fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_chkpath_closed, req);
+}
+
+static void cli_chkpath_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_close_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_chkpath_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_chkpath(struct cli_state *cli, const char *path)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ char *path2 = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ path2 = talloc_strdup(frame, path);
+ if (!path2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ trim_char(path2,'\0','\\');
+ if (!*path2) {
+ path2 = talloc_strdup(frame, "\\");
+ if (!path2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_chkpath_send(frame, ev, cli, path2);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_chkpath_recv(req);
+ cli->raw_status = status; /* cli_smb2_chkpath_recv doesn't set this */
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Query disk space.
+****************************************************************************/
+
+static void cli_dskattr_done(struct tevent_req *subreq);
+
+struct cli_dskattr_state {
+ int bsize;
+ int total;
+ int avail;
+};
+
+struct tevent_req *cli_dskattr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_dskattr_state *state = NULL;
+ uint8_t additional_flags = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_dskattr_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBdskattr, additional_flags, 0,
+ 0, NULL, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_dskattr_done, req);
+ return req;
+}
+
+static void cli_dskattr_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_dskattr_state *state = tevent_req_data(
+ req, struct cli_dskattr_state);
+ uint8_t wct;
+ uint16_t *vwv = NULL;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 4, &wct, &vwv, NULL,
+ NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->bsize = SVAL(vwv+1, 0)*SVAL(vwv+2,0);
+ state->total = SVAL(vwv+0, 0);
+ state->avail = SVAL(vwv+3, 0);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_dskattr_recv(struct tevent_req *req, int *bsize, int *total, int *avail)
+{
+ struct cli_dskattr_state *state = tevent_req_data(
+ req, struct cli_dskattr_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *bsize = state->bsize;
+ *total = state->total;
+ *avail = state->avail;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_dskattr_send(frame, ev, cli);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_dskattr_recv(req, bsize, total, avail);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_disk_size(struct cli_state *cli, const char *path, uint64_t *bsize,
+ uint64_t *total, uint64_t *avail)
+{
+ uint64_t sectors_per_block;
+ uint64_t bytes_per_sector;
+ int old_bsize = 0, old_total = 0, old_avail = 0;
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_dskattr(cli, path, bsize, total, avail);
+ }
+
+ /*
+ * Try the trans2 disk full size info call first.
+ * We already use this in SMBC_fstatvfs_ctx().
+ * Ignore 'actual_available_units' as we only
+ * care about the quota for the caller.
+ */
+
+ status = cli_get_fs_full_size_info(cli,
+ total,
+ avail,
+ NULL,
+ &sectors_per_block,
+ &bytes_per_sector);
+
+ /* Try and cope will all variants of "we don't do this call"
+ and fall back to cli_dskattr. */
+
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_IMPLEMENTED) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_INFO_CLASS) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_PROCEDURE_NOT_FOUND) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_REQUEST) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_STATE) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_CTL_FILE_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status,NT_STATUS_UNSUCCESSFUL)) {
+ goto try_dskattr;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (bsize) {
+ *bsize = sectors_per_block *
+ bytes_per_sector;
+ }
+
+ return NT_STATUS_OK;
+
+ try_dskattr:
+
+ /* Old SMB1 core protocol fallback. */
+ status = cli_dskattr(cli, &old_bsize, &old_total, &old_avail);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (bsize) {
+ *bsize = (uint64_t)old_bsize;
+ }
+ if (total) {
+ *total = (uint64_t)old_total;
+ }
+ if (avail) {
+ *avail = (uint64_t)old_avail;
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Create and open a temporary file.
+****************************************************************************/
+
+static void cli_ctemp_done(struct tevent_req *subreq);
+
+struct ctemp_state {
+ uint16_t vwv[3];
+ char *ret_path;
+ uint16_t fnum;
+};
+
+struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *path)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct ctemp_state *state = NULL;
+ uint8_t additional_flags = 0;
+ uint16_t additional_flags2 = 0;
+ uint8_t *bytes = NULL;
+ char *path_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct ctemp_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(state->vwv,0,0);
+ SIVALS(state->vwv+1,0,-1);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ /*
+ * SMBctemp on a DFS share must use DFS names.
+ */
+ path_cp = smb1_dfs_share_path(state, cli, path);
+ if (tevent_req_nomem(path_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path_cp,
+ strlen(path_cp)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(path)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_smb_send(state, ev, cli, SMBctemp, additional_flags,
+ additional_flags2,
+ 3, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_ctemp_done, req);
+ return req;
+}
+
+static void cli_ctemp_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ctemp_state *state = tevent_req_data(
+ req, struct ctemp_state);
+ NTSTATUS status;
+ uint8_t wcnt;
+ uint16_t *vwv;
+ uint32_t num_bytes = 0;
+ uint8_t *bytes = NULL;
+
+ status = cli_smb_recv(subreq, state, NULL, 1, &wcnt, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->fnum = SVAL(vwv+0, 0);
+
+ /* From W2K3, the result is just the ASCII name */
+ if (num_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_DATA_ERROR);
+ return;
+ }
+
+ if (pull_string_talloc(state,
+ NULL,
+ 0,
+ &state->ret_path,
+ bytes,
+ num_bytes,
+ STR_ASCII) == 0) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_ctemp_recv(struct tevent_req *req,
+ TALLOC_CTX *ctx,
+ uint16_t *pfnum,
+ char **outfile)
+{
+ struct ctemp_state *state = tevent_req_data(req,
+ struct ctemp_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ *outfile = talloc_strdup(ctx, state->ret_path);
+ if (!*outfile) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_ctemp(struct cli_state *cli,
+ TALLOC_CTX *ctx,
+ const char *path,
+ uint16_t *pfnum,
+ char **out_path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_ctemp_send(frame, ev, cli, path);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_ctemp_recv(req, ctx, pfnum, out_path);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*********************************************************
+ Set an extended attribute utility fn.
+*********************************************************/
+
+static NTSTATUS cli_set_ea(struct cli_state *cli, uint16_t setup_val,
+ uint8_t *param, unsigned int param_len,
+ const char *ea_name,
+ const char *ea_val, size_t ea_len)
+{
+ uint16_t setup[1];
+ unsigned int data_len = 0;
+ uint8_t *data = NULL;
+ char *p;
+ size_t ea_namelen = strlen(ea_name);
+ NTSTATUS status;
+
+ SSVAL(setup, 0, setup_val);
+
+ if (ea_namelen == 0 && ea_len == 0) {
+ data_len = 4;
+ data = talloc_array(talloc_tos(),
+ uint8_t,
+ data_len);
+ if (!data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = (char *)data;
+ SIVAL(p,0,data_len);
+ } else {
+ data_len = 4 + 4 + ea_namelen + 1 + ea_len;
+ data = talloc_array(talloc_tos(),
+ uint8_t,
+ data_len);
+ if (!data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = (char *)data;
+ SIVAL(p,0,data_len);
+ p += 4;
+ SCVAL(p, 0, 0); /* EA flags. */
+ SCVAL(p, 1, ea_namelen);
+ SSVAL(p, 2, ea_len);
+ memcpy(p+4, ea_name, ea_namelen+1); /* Copy in the name. */
+ memcpy(p+4+ea_namelen+1, ea_val, ea_len);
+ }
+
+ /*
+ * FIXME - if we want to do previous version path
+ * processing on an EA set call we need to turn this
+ * into calls to cli_trans_send()/cli_trans_recv()
+ * with a temporary event context, as cli_trans_send()
+ * have access to the additional_flags2 needed to
+ * send @GMT- paths. JRA.
+ */
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, -1, 0, 0,
+ setup, 1, 0,
+ param, param_len, 2,
+ data, data_len, 0,
+ NULL,
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ NULL, 0, NULL); /* rdata */
+ talloc_free(data);
+ return status;
+}
+
+/*********************************************************
+ Set an extended attribute on a pathname.
+*********************************************************/
+
+NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path,
+ const char *ea_name, const char *ea_val,
+ size_t ea_len)
+{
+ unsigned int param_len = 0;
+ uint8_t *param;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+ char *path_cp = NULL;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_set_ea_path(cli,
+ path,
+ ea_name,
+ ea_val,
+ ea_len);
+ }
+
+ frame = talloc_stackframe();
+
+ param = talloc_array(frame, uint8_t, 6);
+ if (!param) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ SSVAL(param,0,SMB_INFO_SET_EA);
+ SSVAL(param,2,0);
+ SSVAL(param,4,0);
+
+ /*
+ * TRANSACT2_SETPATHINFO on a DFS share must use DFS names.
+ */
+ path_cp = smb1_dfs_share_path(frame, cli, path);
+ if (path_cp == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ path_cp,
+ strlen(path_cp)+1,
+ NULL);
+ param_len = talloc_get_size(param);
+
+ status = cli_set_ea(cli, TRANSACT2_SETPATHINFO, param, param_len,
+ ea_name, ea_val, ea_len);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*********************************************************
+ Set an extended attribute on an fnum.
+*********************************************************/
+
+NTSTATUS cli_set_ea_fnum(struct cli_state *cli, uint16_t fnum,
+ const char *ea_name, const char *ea_val,
+ size_t ea_len)
+{
+ uint8_t param[6] = { 0, };
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_set_ea_fnum(cli,
+ fnum,
+ ea_name,
+ ea_val,
+ ea_len);
+ }
+
+ SSVAL(param,0,fnum);
+ SSVAL(param,2,SMB_INFO_SET_EA);
+
+ return cli_set_ea(cli, TRANSACT2_SETFILEINFO, param, 6,
+ ea_name, ea_val, ea_len);
+}
+
+/*********************************************************
+ Get an extended attribute list utility fn.
+*********************************************************/
+
+static bool parse_ea_blob(TALLOC_CTX *ctx, const uint8_t *rdata,
+ size_t rdata_len,
+ size_t *pnum_eas, struct ea_struct **pea_list)
+{
+ struct ea_struct *ea_list = NULL;
+ size_t num_eas;
+ size_t ea_size;
+ const uint8_t *p;
+
+ if (rdata_len < 4) {
+ return false;
+ }
+
+ ea_size = (size_t)IVAL(rdata,0);
+ if (ea_size > rdata_len) {
+ return false;
+ }
+
+ if (ea_size == 0) {
+ /* No EA's present. */
+ *pnum_eas = 0;
+ *pea_list = NULL;
+ return true;
+ }
+
+ p = rdata + 4;
+ ea_size -= 4;
+
+ /* Validate the EA list and count it. */
+ for (num_eas = 0; ea_size >= 4; num_eas++) {
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+ if (ea_namelen == 0) {
+ return false;
+ }
+ if (4 + ea_namelen + 1 + ea_valuelen > ea_size) {
+ return false;
+ }
+ ea_size -= 4 + ea_namelen + 1 + ea_valuelen;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ if (num_eas == 0) {
+ *pnum_eas = 0;
+ *pea_list = NULL;
+ return true;
+ }
+
+ *pnum_eas = num_eas;
+ if (!pea_list) {
+ /* Caller only wants number of EA's. */
+ return true;
+ }
+
+ ea_list = talloc_array(ctx, struct ea_struct, num_eas);
+ if (!ea_list) {
+ return false;
+ }
+
+ p = rdata + 4;
+
+ for (num_eas = 0; num_eas < *pnum_eas; num_eas++ ) {
+ struct ea_struct *ea = &ea_list[num_eas];
+ fstring unix_ea_name;
+ unsigned int ea_namelen = CVAL(p,1);
+ unsigned int ea_valuelen = SVAL(p,2);
+
+ ea->flags = CVAL(p,0);
+ unix_ea_name[0] = '\0';
+ pull_ascii(unix_ea_name, p + 4, sizeof(unix_ea_name), rdata_len - PTR_DIFF(p+4, rdata), STR_TERMINATE);
+ ea->name = talloc_strdup(ea_list, unix_ea_name);
+ if (!ea->name) {
+ goto fail;
+ }
+ /* Ensure the value is null terminated (in case it's a string). */
+ ea->value = data_blob_talloc(ea_list, NULL, ea_valuelen + 1);
+ if (!ea->value.data) {
+ goto fail;
+ }
+ if (ea_valuelen) {
+ memcpy(ea->value.data, p+4+ea_namelen+1, ea_valuelen);
+ }
+ ea->value.data[ea_valuelen] = 0;
+ ea->value.length--;
+ p += 4 + ea_namelen + 1 + ea_valuelen;
+ }
+
+ *pea_list = ea_list;
+ return true;
+
+fail:
+ TALLOC_FREE(ea_list);
+ return false;
+}
+
+/*********************************************************
+ Get an extended attribute list from a pathname.
+*********************************************************/
+
+struct cli_get_ea_list_path_state {
+ uint32_t num_data;
+ uint8_t *data;
+};
+
+static void cli_get_ea_list_path_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_get_ea_list_path_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_get_ea_list_path_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_get_ea_list_path_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = cli_qpathinfo_send(state, ev, cli, fname,
+ SMB_INFO_QUERY_ALL_EAS, 4,
+ CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_get_ea_list_path_done, req);
+ return req;
+}
+
+static void cli_get_ea_list_path_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_get_ea_list_path_state *state = tevent_req_data(
+ req, struct cli_get_ea_list_path_state);
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &state->data,
+ &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_get_ea_list_path_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ size_t *pnum_eas, struct ea_struct **peas)
+{
+ struct cli_get_ea_list_path_state *state = tevent_req_data(
+ req, struct cli_get_ea_list_path_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (!parse_ea_blob(mem_ctx, state->data, state->num_data,
+ pnum_eas, peas)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_ea_list_path(struct cli_state *cli, const char *path,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_get_ea_list_path(cli,
+ path,
+ ctx,
+ pnum_eas,
+ pea_list);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_get_ea_list_path_send(frame, ev, cli, path);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_get_ea_list_path_recv(req, ctx, pnum_eas, pea_list);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Convert open "flags" arg to uint32_t on wire.
+****************************************************************************/
+
+static uint32_t open_flags_to_wire(int flags)
+{
+ int open_mode = flags & O_ACCMODE;
+ uint32_t ret = 0;
+
+ switch (open_mode) {
+ case O_WRONLY:
+ ret |= SMB_O_WRONLY;
+ break;
+ case O_RDWR:
+ ret |= SMB_O_RDWR;
+ break;
+ default:
+ case O_RDONLY:
+ ret |= SMB_O_RDONLY;
+ break;
+ }
+
+ if (flags & O_CREAT) {
+ ret |= SMB_O_CREAT;
+ }
+ if (flags & O_EXCL) {
+ ret |= SMB_O_EXCL;
+ }
+ if (flags & O_TRUNC) {
+ ret |= SMB_O_TRUNC;
+ }
+#if defined(O_SYNC)
+ if (flags & O_SYNC) {
+ ret |= SMB_O_SYNC;
+ }
+#endif /* O_SYNC */
+ if (flags & O_APPEND) {
+ ret |= SMB_O_APPEND;
+ }
+#if defined(O_DIRECT)
+ if (flags & O_DIRECT) {
+ ret |= SMB_O_DIRECT;
+ }
+#endif
+#if defined(O_DIRECTORY)
+ if (flags & O_DIRECTORY) {
+ ret |= SMB_O_DIRECTORY;
+ }
+#endif
+ return ret;
+}
+
+/****************************************************************************
+ Open a file - POSIX semantics. Returns fnum. Doesn't request oplock.
+****************************************************************************/
+
+struct cli_posix_open_internal_state {
+ uint16_t setup;
+ uint8_t *param;
+ uint8_t data[18];
+ uint16_t fnum; /* Out */
+};
+
+static void cli_posix_open_internal_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t wire_flags,
+ mode_t mode)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_open_internal_state *state = NULL;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_open_internal_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup setup word. */
+ SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ /* Setup param array. */
+ state->param = talloc_zero_array(state, uint8_t, 6);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ SSVAL(state->param, 0, SMB_POSIX_PATH_OPEN);
+
+ /*
+ * TRANSACT2_SETPATHINFO on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->param = trans2_bytes_push_str(
+ state->param,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SIVAL(state->data,0,0); /* No oplock. */
+ SIVAL(state->data,4,wire_flags);
+ SIVAL(state->data,8,unix_perms_to_wire(mode));
+ SIVAL(state->data,12,0); /* Top bits of perms currently undefined. */
+ SSVAL(state->data,16,SMB_NO_INFO_LEVEL_RETURNED); /* No info level returned. */
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ &state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ talloc_get_size(state->param),/* num param. */
+ 2, /* max returned param. */
+ state->data, /* data. */
+ 18, /* num data. */
+ 12); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_open_internal_done, req);
+ return req;
+}
+
+static void cli_posix_open_internal_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_posix_open_internal_state *state = tevent_req_data(
+ req, struct cli_posix_open_internal_state);
+ NTSTATUS status;
+ uint8_t *data;
+ uint32_t num_data;
+
+ status = cli_trans_recv(
+ subreq,
+ state,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ &data,
+ 12,
+ &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->fnum = SVAL(data,2);
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_posix_open_internal_recv(struct tevent_req *req,
+ uint16_t *pfnum)
+{
+ struct cli_posix_open_internal_state *state = tevent_req_data(
+ req, struct cli_posix_open_internal_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ return NT_STATUS_OK;
+}
+
+struct cli_posix_open_state {
+ uint16_t fnum;
+};
+
+static void cli_posix_open_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ int flags,
+ mode_t mode)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_open_state *state = NULL;
+ uint32_t wire_flags;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_posix_open_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ wire_flags = open_flags_to_wire(flags);
+
+ subreq = cli_posix_open_internal_send(
+ mem_ctx, ev, cli, fname, wire_flags, mode);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_open_done, req);
+ return req;
+}
+
+static void cli_posix_open_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_posix_open_state *state = tevent_req_data(
+ req, struct cli_posix_open_state);
+ NTSTATUS status;
+
+ status = cli_posix_open_internal_recv(subreq, &state->fnum);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_open_recv(struct tevent_req *req, uint16_t *pfnum)
+{
+ struct cli_posix_open_state *state = tevent_req_data(
+ req, struct cli_posix_open_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Open - POSIX semantics. Doesn't request oplock.
+****************************************************************************/
+
+NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname,
+ int flags, mode_t mode, uint16_t *pfnum)
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_posix_open_send(
+ frame, ev, cli, fname, flags, mode);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_posix_open_recv(req, pfnum);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_posix_mkdir_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+};
+
+static void cli_posix_mkdir_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_mkdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_mkdir_state *state = NULL;
+ uint32_t wire_flags;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_mkdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ wire_flags = SMB_O_CREAT | SMB_O_DIRECTORY;
+
+ subreq = cli_posix_open_internal_send(
+ mem_ctx, ev, cli, fname, wire_flags, mode);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_mkdir_done, req);
+ return req;
+}
+
+static void cli_posix_mkdir_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_posix_open_internal_recv(subreq, &fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_posix_mkdir_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_posix_mkdir_send(
+ frame, ev, cli, fname, mode);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_posix_mkdir_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ unlink or rmdir - POSIX semantics.
+****************************************************************************/
+
+struct cli_posix_unlink_internal_state {
+ uint8_t data[2];
+};
+
+static void cli_posix_unlink_internal_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_posix_unlink_internal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint16_t level)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_unlink_internal_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_posix_unlink_internal_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup data word. */
+ SSVAL(state->data, 0, level);
+
+ subreq = cli_setpathinfo_send(state, ev, cli,
+ SMB_POSIX_PATH_UNLINK,
+ fname,
+ state->data, sizeof(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_unlink_internal_done, req);
+ return req;
+}
+
+static void cli_posix_unlink_internal_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setpathinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_posix_unlink_internal_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_posix_unlink_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_unlink_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_unlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_unlink_state *state;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_posix_unlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = cli_posix_unlink_internal_send(
+ mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_FILE_TARGET);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_unlink_done, req);
+ return req;
+}
+
+static void cli_posix_unlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_unlink_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_unlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ unlink - POSIX semantics.
+****************************************************************************/
+
+NTSTATUS cli_posix_unlink(struct cli_state *cli, const char *fname)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_unlink_send(frame,
+ ev,
+ cli,
+ fname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_unlink_recv(req);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ rmdir - POSIX semantics.
+****************************************************************************/
+
+struct cli_posix_rmdir_state {
+ uint8_t dummy;
+};
+
+static void cli_posix_rmdir_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_rmdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_posix_rmdir_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_posix_rmdir_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = cli_posix_unlink_internal_send(
+ mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_DIRECTORY_TARGET);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_rmdir_done, req);
+ return req;
+}
+
+static void cli_posix_rmdir_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_posix_unlink_internal_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_rmdir_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_rmdir(struct cli_state *cli, const char *fname)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_rmdir_send(frame,
+ ev,
+ cli,
+ fname);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_rmdir_recv(req, frame);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ filechangenotify
+****************************************************************************/
+
+struct cli_notify_state {
+ struct tevent_req *subreq;
+ uint8_t setup[8];
+ uint32_t num_changes;
+ struct notify_change *changes;
+};
+
+static void cli_notify_done(struct tevent_req *subreq);
+static void cli_notify_done_smb2(struct tevent_req *subreq);
+static bool cli_notify_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint32_t buffer_size,
+ uint32_t completion_filter, bool recursive)
+{
+ struct tevent_req *req;
+ struct cli_notify_state *state;
+ unsigned old_timeout;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_notify_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /*
+ * Notifies should not time out
+ */
+ old_timeout = cli_set_timeout(cli, 0);
+
+ state->subreq = cli_smb2_notify_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ buffer_size,
+ completion_filter,
+ recursive);
+
+ cli_set_timeout(cli, old_timeout);
+
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->subreq, cli_notify_done_smb2, req);
+ goto done;
+ }
+
+ SIVAL(state->setup, 0, completion_filter);
+ SSVAL(state->setup, 4, fnum);
+ SSVAL(state->setup, 6, recursive);
+
+ /*
+ * Notifies should not time out
+ */
+ old_timeout = cli_set_timeout(cli, 0);
+
+ state->subreq = cli_trans_send(
+ state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBnttrans, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ NT_TRANSACT_NOTIFY_CHANGE, /* function. */
+ 0, /* flags. */
+ (uint16_t *)state->setup, /* setup. */
+ 4, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ NULL, /* param. */
+ 0, /* num param. */
+ buffer_size, /* max returned param. */
+ NULL, /* data. */
+ 0, /* num data. */
+ 0); /* max returned data. */
+
+ cli_set_timeout(cli, old_timeout);
+
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, cli_notify_done, req);
+done:
+ tevent_req_set_cancel_fn(req, cli_notify_cancel);
+ return req;
+}
+
+static bool cli_notify_cancel(struct tevent_req *req)
+{
+ struct cli_notify_state *state = tevent_req_data(
+ req, struct cli_notify_state);
+ bool ok;
+
+ ok = tevent_req_cancel(state->subreq);
+ return ok;
+}
+
+static void cli_notify_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_notify_state *state = tevent_req_data(
+ req, struct cli_notify_state);
+ NTSTATUS status;
+ uint8_t *params;
+ uint32_t i, ofs, num_params;
+ uint16_t flags2;
+
+ status = cli_trans_recv(subreq, talloc_tos(), &flags2, NULL, 0, NULL,
+ &params, 0, &num_params, NULL, 0, NULL);
+ TALLOC_FREE(subreq);
+ state->subreq = NULL;
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("cli_trans_recv returned %s\n", nt_errstr(status)));
+ return;
+ }
+
+ state->num_changes = 0;
+ ofs = 0;
+
+ while (num_params - ofs > 12) {
+ uint32_t next = IVAL(params, ofs);
+ state->num_changes += 1;
+
+ if ((next == 0) || (ofs+next >= num_params)) {
+ break;
+ }
+ ofs += next;
+ }
+
+ state->changes = talloc_array(state, struct notify_change,
+ state->num_changes);
+ if (tevent_req_nomem(state->changes, req)) {
+ TALLOC_FREE(params);
+ return;
+ }
+
+ ofs = 0;
+
+ for (i=0; i<state->num_changes; i++) {
+ uint32_t next = IVAL(params, ofs);
+ uint32_t len = IVAL(params, ofs+8);
+ ssize_t ret;
+ char *name;
+
+ if (smb_buffer_oob(num_params, ofs + 12, len)) {
+ TALLOC_FREE(params);
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->changes[i].action = IVAL(params, ofs+4);
+ ret = pull_string_talloc(state->changes,
+ (char *)params,
+ flags2,
+ &name,
+ params+ofs+12,
+ len,
+ STR_TERMINATE|STR_UNICODE);
+ if (ret == -1) {
+ TALLOC_FREE(params);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ state->changes[i].name = name;
+ ofs += next;
+ }
+
+ TALLOC_FREE(params);
+ tevent_req_done(req);
+}
+
+static void cli_notify_done_smb2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_notify_state *state = tevent_req_data(
+ req, struct cli_notify_state);
+ NTSTATUS status;
+
+ status = cli_smb2_notify_recv(
+ subreq,
+ state,
+ &state->changes,
+ &state->num_changes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_changes,
+ struct notify_change **pchanges)
+{
+ struct cli_notify_state *state = tevent_req_data(
+ req, struct cli_notify_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *pnum_changes = state->num_changes;
+ *pchanges = talloc_move(mem_ctx, &state->changes);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_notify(struct cli_state *cli, uint16_t fnum, uint32_t buffer_size,
+ uint32_t completion_filter, bool recursive,
+ TALLOC_CTX *mem_ctx, uint32_t *pnum_changes,
+ struct notify_change **pchanges)
+{
+ TALLOC_CTX *frame;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_notify_send(ev, ev, cli, fnum, buffer_size,
+ completion_filter, recursive);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_notify_recv(req, mem_ctx, pnum_changes, pchanges);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_qpathinfo_state {
+ uint8_t *param;
+ uint8_t *data;
+ uint16_t setup[1];
+ uint32_t min_rdata;
+ uint8_t *rdata;
+ uint32_t num_rdata;
+};
+
+static void cli_qpathinfo_done(struct tevent_req *subreq);
+static void cli_qpathinfo_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_qpathinfo_state *state;
+ uint16_t additional_flags2 = 0;
+ char *fname_cp = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ uint16_t smb2_level = 0;
+
+ switch (level) {
+ case SMB_QUERY_FILE_ALT_NAME_INFO:
+ smb2_level = FSCC_FILE_ALTERNATE_NAME_INFORMATION;
+ break;
+ default:
+ tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb2_qpathinfo_send(state,
+ ev,
+ cli,
+ fname,
+ smb2_level,
+ min_rdata,
+ max_rdata);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo_done2, req);
+ return req;
+ }
+
+ state->min_rdata = min_rdata;
+ SSVAL(state->setup, 0, TRANSACT2_QPATHINFO);
+
+ state->param = talloc_zero_array(state, uint8_t, 6);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ SSVAL(state->param, 0, level);
+ /*
+ * qpathinfo on a DFS share must use DFS names.
+ */
+ fname_cp = smb1_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname_cp, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname_cp,
+ strlen(fname_cp)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(fname) &&
+ !INFO_LEVEL_IS_UNIX(level)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_trans_send(
+ state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ additional_flags2, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ talloc_get_size(state->param), /* num param. */
+ 2, /* max returned param. */
+ NULL, /* data. */
+ 0, /* num data. */
+ max_rdata); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo_done, req);
+ return req;
+}
+
+static void cli_qpathinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qpathinfo_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_state);
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
+ NULL, 0, NULL,
+ &state->rdata, state->min_rdata,
+ &state->num_rdata);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_qpathinfo_done2(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_qpathinfo_state);
+ NTSTATUS status;
+
+ status = cli_smb2_qpathinfo_recv(subreq,
+ state,
+ &state->rdata,
+ &state->num_rdata);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_qpathinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **rdata, uint32_t *num_rdata)
+{
+ struct cli_qpathinfo_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (rdata != NULL) {
+ *rdata = talloc_move(mem_ctx, &state->rdata);
+ } else {
+ TALLOC_FREE(state->rdata);
+ }
+ if (num_rdata != NULL) {
+ *num_rdata = state->num_rdata;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_qpathinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ const char *fname, uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata,
+ uint8_t **rdata, uint32_t *num_rdata)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qpathinfo_send(frame, ev, cli, fname, level, min_rdata,
+ max_rdata);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_qpathinfo_recv(req, mem_ctx, rdata, num_rdata);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_qfileinfo_state {
+ uint16_t setup[1];
+ uint8_t param[4];
+ uint8_t *data;
+ uint16_t recv_flags2;
+ uint32_t min_rdata;
+ uint8_t *rdata;
+ uint32_t num_rdata;
+};
+
+static void cli_qfileinfo_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_qfileinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_qfileinfo_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_qfileinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->min_rdata = min_rdata;
+ SSVAL(state->param, 0, fnum);
+ SSVAL(state->param, 2, level);
+ SSVAL(state->setup, 0, TRANSACT2_QFILEINFO);
+
+ subreq = cli_trans_send(
+ state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ sizeof(state->param), /* num param. */
+ 2, /* max returned param. */
+ NULL, /* data. */
+ 0, /* num data. */
+ max_rdata); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qfileinfo_done, req);
+ return req;
+}
+
+static void cli_qfileinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qfileinfo_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_state);
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, state,
+ &state->recv_flags2,
+ NULL, 0, NULL,
+ NULL, 0, NULL,
+ &state->rdata, state->min_rdata,
+ &state->num_rdata);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_qfileinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint16_t *recv_flags2,
+ uint8_t **rdata, uint32_t *num_rdata)
+{
+ struct cli_qfileinfo_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (recv_flags2 != NULL) {
+ *recv_flags2 = state->recv_flags2;
+ }
+ if (rdata != NULL) {
+ *rdata = talloc_move(mem_ctx, &state->rdata);
+ }
+ if (num_rdata != NULL) {
+ *num_rdata = state->num_rdata;
+ }
+
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_qfileinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint16_t fnum, uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata, uint16_t *recv_flags2,
+ uint8_t **rdata, uint32_t *num_rdata)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qfileinfo_send(frame, ev, cli, fnum, level, min_rdata,
+ max_rdata);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_qfileinfo_recv(req, mem_ctx, recv_flags2, rdata, num_rdata);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_flush_state {
+ uint16_t vwv[1];
+};
+
+static void cli_flush_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_flush_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_flush_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_flush_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ SSVAL(state->vwv + 0, 0, fnum);
+
+ subreq = cli_smb_send(state, ev, cli, SMBflush, 0, 0, 1, state->vwv,
+ 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_flush_done, req);
+ return req;
+}
+
+static void cli_flush_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_flush_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_flush(TALLOC_CTX *mem_ctx, struct cli_state *cli, uint16_t fnum)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_flush_send(frame, ev, cli, fnum);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_flush_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_shadow_copy_data_state {
+ uint16_t setup[4];
+ uint8_t *data;
+ uint32_t num_data;
+ bool get_names;
+};
+
+static void cli_shadow_copy_data_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_shadow_copy_data_state *state;
+ uint32_t ret_size;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_shadow_copy_data_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->get_names = get_names;
+ ret_size = get_names ? CLI_BUFFER_SIZE : 16;
+
+ SIVAL(state->setup + 0, 0, FSCTL_GET_SHADOW_COPY_DATA);
+ SSVAL(state->setup + 2, 0, fnum);
+ SCVAL(state->setup + 3, 0, 1); /* isFsctl */
+ SCVAL(state->setup + 3, 1, 0); /* compfilter, isFlags (WSSP) */
+
+ subreq = cli_trans_send(
+ state, ev, cli, 0, SMBnttrans, NULL, 0, NT_TRANSACT_IOCTL, 0,
+ state->setup, ARRAY_SIZE(state->setup),
+ ARRAY_SIZE(state->setup),
+ NULL, 0, 0,
+ NULL, 0, ret_size);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_shadow_copy_data_done, req);
+ return req;
+}
+
+static void cli_shadow_copy_data_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_shadow_copy_data_state *state = tevent_req_data(
+ req, struct cli_shadow_copy_data_state);
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, state, NULL,
+ NULL, 0, NULL, /* setup */
+ NULL, 0, NULL, /* param */
+ &state->data, 12, &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char ***pnames, int *pnum_names)
+{
+ struct cli_shadow_copy_data_state *state = tevent_req_data(
+ req, struct cli_shadow_copy_data_state);
+ char **names = NULL;
+ uint32_t i, num_names;
+ uint32_t dlength;
+ uint8_t *endp = NULL;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->num_data < 16) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ num_names = IVAL(state->data, 4);
+ dlength = IVAL(state->data, 8);
+
+ if (num_names > 0x7FFFFFFF) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (!state->get_names) {
+ *pnum_names = (int)num_names;
+ return NT_STATUS_OK;
+ }
+
+ if (dlength + 12 < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (dlength + 12 > state->num_data) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (state->num_data + (2 * sizeof(SHADOW_COPY_LABEL)) <
+ state->num_data) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ names = talloc_array(mem_ctx, char *, num_names);
+ if (names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ endp = state->data + state->num_data;
+
+ for (i=0; i<num_names; i++) {
+ bool ret;
+ uint8_t *src;
+ size_t converted_size;
+
+ src = state->data + 12 + i * 2 * sizeof(SHADOW_COPY_LABEL);
+
+ if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ ret = convert_string_talloc(
+ names, CH_UTF16LE, CH_UNIX,
+ src, 2 * sizeof(SHADOW_COPY_LABEL),
+ &names[i], &converted_size);
+ if (!ret) {
+ TALLOC_FREE(names);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *pnum_names = (int)num_names;
+ *pnames = names;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint16_t fnum, bool get_names,
+ char ***pnames, int *pnum_names)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_shadow_copy_data(mem_ctx,
+ cli,
+ fnum,
+ get_names,
+ pnames,
+ pnum_names);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_shadow_copy_data_send(frame, ev, cli, fnum, get_names);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_shadow_copy_data_recv(req, mem_ctx, pnames, pnum_names);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_fsctl_state {
+ DATA_BLOB out;
+};
+
+static void cli_fsctl_smb1_done(struct tevent_req *subreq);
+static void cli_fsctl_smb2_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_fsctl_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t ctl_code,
+ const DATA_BLOB *in,
+ uint32_t max_out)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_fsctl_state *state = NULL;
+ uint16_t *setup = NULL;
+ uint8_t *data = NULL;
+ uint32_t num_data = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_fsctl_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_fsctl_send(
+ state, ev, cli, fnum, ctl_code, in, max_out);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_fsctl_smb2_done, req);
+ return req;
+ }
+
+ setup = talloc_array(state, uint16_t, 4);
+ if (tevent_req_nomem(setup, req)) {
+ return tevent_req_post(req, ev);
+ }
+ SIVAL(setup, 0, ctl_code);
+ SSVAL(setup, 4, fnum);
+ SCVAL(setup, 6, 1); /* IsFcntl */
+ SCVAL(setup, 7, 0); /* IsFlags */
+
+ if (in) {
+ data = in->data;
+ num_data = in->length;
+ }
+
+ subreq = cli_trans_send(state,
+ ev,
+ cli,
+ 0, /* additional_flags2 */
+ SMBnttrans, /* cmd */
+ NULL, /* name */
+ -1, /* fid */
+ NT_TRANSACT_IOCTL, /* function */
+ 0, /* flags */
+ setup,
+ 4,
+ 0, /* setup */
+ NULL,
+ 0,
+ 0, /* param */
+ data,
+ num_data,
+ max_out); /* data */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_fsctl_smb1_done, req);
+ return req;
+}
+
+static void cli_fsctl_smb2_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_fsctl_state *state = tevent_req_data(
+ req, struct cli_fsctl_state);
+ NTSTATUS status;
+
+ status = cli_smb2_fsctl_recv(subreq, state, &state->out);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_fsctl_smb1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_fsctl_state *state = tevent_req_data(
+ req, struct cli_fsctl_state);
+ uint8_t *out = NULL;
+ uint32_t out_len;
+ NTSTATUS status;
+
+ status = cli_trans_recv(
+ subreq, state, NULL,
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ &out, 0, &out_len);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->out = (DATA_BLOB) {
+ .data = out, .length = out_len,
+ };
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_fsctl_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
+{
+ struct cli_fsctl_state *state = tevent_req_data(
+ req, struct cli_fsctl_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (out != NULL) {
+ *out = (DATA_BLOB) {
+ .data = talloc_move(mem_ctx, &state->out.data),
+ .length = state->out.length,
+ };
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c
new file mode 100644
index 0000000..cc95f47
--- /dev/null
+++ b/source3/libsmb/clifsinfo.c
@@ -0,0 +1,815 @@
+/*
+ Unix SMB/CIFS implementation.
+ FS info functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+ Copyright (C) Jeremy Allison 2007
+ Copyright (C) Andrew Bartlett 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "trans2.h"
+#include "auth_generic.h"
+#include "auth/gensec/gensec.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "auth/credentials/credentials.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+
+/****************************************************************************
+ Get UNIX extensions version info.
+****************************************************************************/
+
+struct cli_unix_extensions_version_state {
+ struct cli_state *cli;
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+};
+
+static void cli_unix_extensions_version_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_unix_extensions_version_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_unix_extensions_version_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ SSVAL(state->setup, 0, TRANSACT2_QFSINFO);
+ SSVAL(state->param, 0, SMB_QUERY_CIFS_UNIX_INFO);
+
+ subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2,
+ NULL, 0, 0, 0,
+ state->setup, 1, 0,
+ state->param, 2, 0,
+ NULL, 0, 560);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_unix_extensions_version_done, req);
+ return req;
+}
+
+static void cli_unix_extensions_version_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_unix_extensions_version_state *state = tevent_req_data(
+ req, struct cli_unix_extensions_version_state);
+ uint8_t *data;
+ uint32_t num_data;
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
+ NULL, 0, NULL, &data, 12, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->major = SVAL(data, 0);
+ state->minor = SVAL(data, 2);
+ state->caplow = IVAL(data, 4);
+ state->caphigh = IVAL(data, 8);
+ TALLOC_FREE(data);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req,
+ uint16_t *pmajor, uint16_t *pminor,
+ uint32_t *pcaplow,
+ uint32_t *pcaphigh)
+{
+ struct cli_unix_extensions_version_state *state = tevent_req_data(
+ req, struct cli_unix_extensions_version_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pmajor = state->major;
+ *pminor = state->minor;
+ *pcaplow = state->caplow;
+ *pcaphigh = state->caphigh;
+ state->cli->server_posix_capabilities = *pcaplow;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16_t *pmajor,
+ uint16_t *pminor, uint32_t *pcaplow,
+ uint32_t *pcaphigh)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_unix_extensions_version_send(frame, ev, cli);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_unix_extensions_version_recv(req, pmajor, pminor, pcaplow,
+ pcaphigh);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Set UNIX extensions capabilities.
+****************************************************************************/
+
+struct cli_set_unix_extensions_capabilities_state {
+ struct cli_state *cli;
+ uint16_t setup[1];
+ uint8_t param[4];
+ uint8_t data[12];
+};
+
+static void cli_set_unix_extensions_capabilities_done(
+ struct tevent_req *subreq);
+
+struct tevent_req *cli_set_unix_extensions_capabilities_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ uint16_t major, uint16_t minor, uint32_t caplow, uint32_t caphigh)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_set_unix_extensions_capabilities_state *state;
+
+ req = tevent_req_create(
+ mem_ctx, &state,
+ struct cli_set_unix_extensions_capabilities_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->cli = cli;
+ SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO);
+
+ SSVAL(state->param, 0, 0);
+ SSVAL(state->param, 2, SMB_SET_CIFS_UNIX_INFO);
+
+ SSVAL(state->data, 0, major);
+ SSVAL(state->data, 2, minor);
+ SIVAL(state->data, 4, caplow);
+ SIVAL(state->data, 8, caphigh);
+
+ subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2,
+ NULL, 0, 0, 0,
+ state->setup, 1, 0,
+ state->param, 4, 0,
+ state->data, 12, 560);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_set_unix_extensions_capabilities_done, req);
+ return req;
+}
+
+static void cli_set_unix_extensions_capabilities_done(
+ struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_set_unix_extensions_capabilities_state *state = tevent_req_data(
+ req, struct cli_set_unix_extensions_capabilities_state);
+
+ NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
+ NULL, 0, NULL, NULL, 0, NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ state->cli->requested_posix_capabilities = IVAL(state->data, 4);
+ }
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli,
+ uint16_t major, uint16_t minor,
+ uint32_t caplow, uint32_t caphigh)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_set_unix_extensions_capabilities_send(
+ ev, ev, cli, major, minor, caplow, caphigh);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_set_unix_extensions_capabilities_recv(req);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+struct cli_get_fs_attr_info_state {
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint32_t fs_attr;
+};
+
+static void cli_get_fs_attr_info_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *subreq, *req;
+ struct cli_get_fs_attr_info_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_get_fs_attr_info_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ SSVAL(state->setup+0, 0, TRANSACT2_QFSINFO);
+ SSVAL(state->param+0, 0, SMB_QUERY_FS_ATTRIBUTE_INFO);
+
+ subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2,
+ NULL, 0, 0, 0,
+ state->setup, 1, 0,
+ state->param, 2, 0,
+ NULL, 0, 560);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_get_fs_attr_info_done, req);
+ return req;
+}
+
+static void cli_get_fs_attr_info_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_get_fs_attr_info_state *state = tevent_req_data(
+ req, struct cli_get_fs_attr_info_state);
+ uint8_t *data;
+ uint32_t num_data;
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq, talloc_tos(), NULL, NULL, 0, NULL,
+ NULL, 0, NULL, &data, 12, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->fs_attr = IVAL(data, 0);
+ TALLOC_FREE(data);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr)
+{
+ struct cli_get_fs_attr_info_state *state = tevent_req_data(
+ req, struct cli_get_fs_attr_info_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *fs_attr = state->fs_attr;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_get_fs_attr_info(cli, fs_attr);
+ }
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_get_fs_attr_info_send(ev, ev, cli);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_get_fs_attr_info_recv(req, fs_attr);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+NTSTATUS cli_get_fs_volume_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ char **_volume_name,
+ uint32_t *pserial_number,
+ time_t *pdate)
+{
+ NTSTATUS status;
+ uint16_t recv_flags2;
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint8_t *rdata;
+ uint32_t rdata_count;
+ unsigned int nlen;
+ char *volume_name = NULL;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_get_fs_volume_info(cli,
+ mem_ctx,
+ _volume_name,
+ pserial_number,
+ pdate);
+ }
+
+ SSVAL(setup, 0, TRANSACT2_QFSINFO);
+ SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, 0, 0, 0,
+ setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560,
+ &recv_flags2,
+ NULL, 0, NULL,
+ NULL, 0, NULL,
+ &rdata, 18, &rdata_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pdate) {
+ struct timespec ts;
+ ts = interpret_long_date(BVAL(rdata, 0));
+ *pdate = ts.tv_sec;
+ }
+ if (pserial_number) {
+ *pserial_number = IVAL(rdata,8);
+ }
+ nlen = IVAL(rdata,12);
+ if (nlen > (rdata_count - 18)) {
+ TALLOC_FREE(rdata);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ pull_string_talloc(mem_ctx,
+ (const char *)rdata,
+ recv_flags2,
+ &volume_name,
+ rdata + 18,
+ nlen, STR_UNICODE);
+ if (volume_name == NULL) {
+ status = map_nt_error_from_unix(errno);
+ TALLOC_FREE(rdata);
+ return status;
+ }
+
+ /* todo: but not yet needed
+ * return the other stuff
+ */
+
+ *_volume_name = volume_name;
+ TALLOC_FREE(rdata);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_fs_full_size_info(struct cli_state *cli,
+ uint64_t *total_allocation_units,
+ uint64_t *caller_allocation_units,
+ uint64_t *actual_allocation_units,
+ uint64_t *sectors_per_allocation_unit,
+ uint64_t *bytes_per_sector)
+{
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint8_t *rdata = NULL;
+ uint32_t rdata_count;
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_get_fs_full_size_info(cli,
+ total_allocation_units,
+ caller_allocation_units,
+ actual_allocation_units,
+ sectors_per_allocation_unit,
+ bytes_per_sector);
+ }
+
+ SSVAL(setup, 0, TRANSACT2_QFSINFO);
+ SSVAL(param, 0, SMB_FS_FULL_SIZE_INFORMATION);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, 0, 0, 0,
+ setup, 1, 0, /* setup */
+ param, 2, 0, /* param */
+ NULL, 0, 560, /* data */
+ NULL,
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ &rdata, 32, &rdata_count); /* rdata */
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (total_allocation_units) {
+ *total_allocation_units = BIG_UINT(rdata, 0);
+ }
+ if (caller_allocation_units) {
+ *caller_allocation_units = BIG_UINT(rdata,8);
+ }
+ if (actual_allocation_units) {
+ *actual_allocation_units = BIG_UINT(rdata,16);
+ }
+ if (sectors_per_allocation_unit) {
+ *sectors_per_allocation_unit = IVAL(rdata,24);
+ }
+ if (bytes_per_sector) {
+ *bytes_per_sector = IVAL(rdata,28);
+ }
+
+fail:
+ TALLOC_FREE(rdata);
+ return status;
+}
+
+NTSTATUS cli_get_posix_fs_info(struct cli_state *cli,
+ uint32_t *optimal_transfer_size,
+ uint32_t *block_size,
+ uint64_t *total_blocks,
+ uint64_t *blocks_available,
+ uint64_t *user_blocks_available,
+ uint64_t *total_file_nodes,
+ uint64_t *free_file_nodes,
+ uint64_t *fs_identifier)
+{
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint8_t *rdata = NULL;
+ uint32_t rdata_count;
+ NTSTATUS status;
+
+ SSVAL(setup, 0, TRANSACT2_QFSINFO);
+ SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, 0, 0, 0,
+ setup, 1, 0,
+ param, 2, 0,
+ NULL, 0, 560,
+ NULL,
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ &rdata, 56, &rdata_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (optimal_transfer_size) {
+ *optimal_transfer_size = IVAL(rdata, 0);
+ }
+ if (block_size) {
+ *block_size = IVAL(rdata,4);
+ }
+ if (total_blocks) {
+ *total_blocks = BIG_UINT(rdata,8);
+ }
+ if (blocks_available) {
+ *blocks_available = BIG_UINT(rdata,16);
+ }
+ if (user_blocks_available) {
+ *user_blocks_available = BIG_UINT(rdata,24);
+ }
+ if (total_file_nodes) {
+ *total_file_nodes = BIG_UINT(rdata,32);
+ }
+ if (free_file_nodes) {
+ *free_file_nodes = BIG_UINT(rdata,40);
+ }
+ if (fs_identifier) {
+ *fs_identifier = BIG_UINT(rdata,48);
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a UNIX extensions SMB_QUERY_POSIX_WHOAMI call.
+****************************************************************************/
+
+struct posix_whoami_state {
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint32_t max_rdata;
+ bool guest;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t num_gids;
+ uint64_t *gids;
+ uint32_t num_sids;
+ struct dom_sid *sids;
+};
+
+static void cli_posix_whoami_done(struct tevent_req *subreq);
+
+static const uint32_t posix_whoami_max_rdata = 62*1024;
+
+struct tevent_req *cli_posix_whoami_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct posix_whoami_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct posix_whoami_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /* Setup setup word. */
+ SSVAL(state->setup, 0, TRANSACT2_QFSINFO);
+ SSVAL(state->param, 0, SMB_QUERY_POSIX_WHOAMI);
+
+ state->max_rdata = posix_whoami_max_rdata;
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev, /* event ctx. */
+ cli, /* cli_state. */
+ 0, /* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL, /* pipe name. */
+ -1, /* fid. */
+ 0, /* function. */
+ 0, /* flags. */
+ state->setup, /* setup. */
+ 1, /* num setup uint16_t words. */
+ 0, /* max returned setup. */
+ state->param, /* param. */
+ 2, /* num param. */
+ 0, /* max returned param. */
+ NULL, /* data. */
+ 0, /* num data. */
+ state->max_rdata); /* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_posix_whoami_done, req);
+ return req;
+}
+
+static void cli_posix_whoami_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct posix_whoami_state *state = tevent_req_data(
+ req, struct posix_whoami_state);
+ uint8_t *rdata = NULL;
+ uint8_t *p = NULL;
+ uint32_t num_rdata = 0;
+ uint32_t i;
+ NTSTATUS status;
+
+ status = cli_trans_recv(subreq,
+ state,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ &rdata,
+ 40,
+ &num_rdata);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /*
+ * Not strictly needed - cli_trans_recv()
+ * will ensure at least 40 bytes here. Added
+ * as more of a reminder to be careful when
+ * parsing network packets in C.
+ */
+
+ if (num_rdata < 40 || num_rdata > posix_whoami_max_rdata) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->guest = (IVAL(rdata, 0) & SMB_WHOAMI_GUEST);
+ state->uid = BVAL(rdata, 8);
+ state->gid = BVAL(rdata, 16);
+ state->num_gids = IVAL(rdata, 24);
+ state->num_sids = IVAL(rdata, 28);
+
+ /* Ensure the gid array doesn't overflow */
+ if (state->num_gids > (num_rdata - 40) / sizeof(uint64_t)) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->gids = talloc_array(state, uint64_t, state->num_gids);
+ if (tevent_req_nomem(state->gids, req)) {
+ return;
+ }
+ state->sids = talloc_array(state, struct dom_sid, state->num_sids);
+ if (tevent_req_nomem(state->sids, req)) {
+ return;
+ }
+
+ p = rdata + 40;
+
+ for (i = 0; i < state->num_gids; i++) {
+ state->gids[i] = BVAL(p, 0);
+ p += 8;
+ }
+
+ num_rdata -= (p - rdata);
+
+ for (i = 0; i < state->num_sids; i++) {
+ size_t sid_size;
+ DATA_BLOB in = data_blob_const(p, num_rdata);
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_pull_struct_blob(&in,
+ state,
+ &state->sids[i],
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ sid_size = ndr_size_dom_sid(&state->sids[i], 0);
+
+ if (sid_size > num_rdata) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ p += sid_size;
+ num_rdata -= sid_size;
+ }
+
+ if (num_rdata != 0) {
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_posix_whoami_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint64_t *puid,
+ uint64_t *pgid,
+ uint32_t *pnum_gids,
+ uint64_t **pgids,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids,
+ bool *pguest)
+{
+ NTSTATUS status;
+ struct posix_whoami_state *state = tevent_req_data(
+ req, struct posix_whoami_state);
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (puid) {
+ *puid = state->uid;
+ }
+ if (pgid) {
+ *pgid = state->gid;
+ }
+ if (pnum_gids) {
+ *pnum_gids = state->num_gids;
+ }
+ if (pgids) {
+ *pgids = talloc_move(mem_ctx, &state->gids);
+ }
+ if (pnum_sids) {
+ *pnum_sids = state->num_sids;
+ }
+ if (psids) {
+ *psids = talloc_move(mem_ctx, &state->sids);
+ }
+ if (pguest) {
+ *pguest = state->guest;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_posix_whoami(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint64_t *puid,
+ uint64_t *pgid,
+ uint32_t *num_gids,
+ uint64_t **gids,
+ uint32_t *num_sids,
+ struct dom_sid **sids,
+ bool *pguest)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_posix_whoami_send(frame,
+ ev,
+ cli);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_posix_whoami_recv(req,
+ mem_ctx,
+ puid,
+ pgid,
+ num_gids,
+ gids,
+ num_sids,
+ sids,
+ pguest);
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c
new file mode 100644
index 0000000..54b46b0
--- /dev/null
+++ b/source3/libsmb/clilist.c
@@ -0,0 +1,1255 @@
+/*
+ Unix SMB/CIFS implementation.
+ client directory list routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/****************************************************************************
+ Check if a returned directory name is safe.
+****************************************************************************/
+
+static NTSTATUS is_bad_name(bool windows_names, const char *name)
+{
+ const char *bad_name_p = NULL;
+
+ bad_name_p = strchr(name, '/');
+ if (bad_name_p != NULL) {
+ /*
+ * Windows and POSIX names can't have '/'.
+ * Server is attacking us.
+ */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (windows_names) {
+ bad_name_p = strchr(name, '\\');
+ if (bad_name_p != NULL) {
+ /*
+ * Windows names can't have '\\'.
+ * Server is attacking us.
+ */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Check if a returned directory name is safe. Disconnect if server is
+ sending bad names.
+****************************************************************************/
+
+NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
+ const struct file_info *finfo)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ bool windows_names = true;
+
+ if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ windows_names = false;
+ }
+ if (finfo->name != NULL) {
+ status = is_bad_name(windows_names, finfo->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("bad finfo->name\n");
+ return status;
+ }
+ }
+ if (finfo->short_name != NULL) {
+ status = is_bad_name(windows_names, finfo->short_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("bad finfo->short_name\n");
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Calculate a safe next_entry_offset.
+****************************************************************************/
+
+static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
+{
+ size_t next_entry_offset = (size_t)IVAL(base,0);
+
+ if (next_entry_offset == 0 ||
+ base + next_entry_offset < base ||
+ base + next_entry_offset > pdata_end) {
+ next_entry_offset = pdata_end - base;
+ }
+ return next_entry_offset;
+}
+
+/****************************************************************************
+ Interpret a long filename structure - this is mostly guesses at the moment.
+ The length of the structure is returned
+ The structure of a long filename depends on the info level.
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
+ by NT and SMB_FIND_EA_SIZE is used by OS/2
+****************************************************************************/
+
+static size_t interpret_long_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ int level,
+ const char *base_ptr,
+ uint16_t recv_flags2,
+ const char *p,
+ const char *pdata_end,
+ struct file_info *finfo,
+ uint32_t *p_resume_key,
+ DATA_BLOB *p_last_name_raw)
+{
+ int len;
+ size_t ret;
+ const char *base = p;
+
+ data_blob_free(p_last_name_raw);
+
+ if (p_resume_key) {
+ *p_resume_key = 0;
+ }
+ ZERO_STRUCTP(finfo);
+
+ switch (level) {
+ case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ if (pdata_end - base < 27) {
+ return pdata_end - base;
+ }
+ /*
+ * What we're returning here as ctime_ts is
+ * actually the server create time.
+ */
+ finfo->btime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4,
+ smb1cli_conn_server_time_zone(
+ cli->conn)));
+ finfo->ctime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->atime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->mtime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->size = IVAL(p,16);
+ finfo->attr = SVAL(p,24);
+ len = CVAL(p, 26);
+ p += 27;
+ if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
+ p += ucs2_align(base_ptr, p, STR_UNICODE);
+ }
+
+ /* We can safely use len here (which is required by OS/2)
+ * and the NAS-BASIC server instead of +2 or +1 as the
+ * STR_TERMINATE flag below is
+ * actually used as the length calculation.
+ * The len is merely an upper bound.
+ * Due to the explicit 2 byte null termination
+ * in cli_receive_trans/cli_receive_nt_trans
+ * we know this is safe. JRA + kukks
+ */
+
+ if (p + len > pdata_end) {
+ return pdata_end - base;
+ }
+
+ /* the len+2 below looks strange but it is
+ important to cope with the differences
+ between win2000 and win9x for this call
+ (tridge) */
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->name,
+ p,
+ len+2,
+ STR_TERMINATE);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += ret;
+ return PTR_DIFF(p, base);
+
+ case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
+ /* these dates are converted to GMT by
+ make_unix_date */
+ if (pdata_end - base < 31) {
+ return pdata_end - base;
+ }
+ /*
+ * What we're returning here as ctime_ts is
+ * actually the server create time.
+ */
+ finfo->btime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4,
+ smb1cli_conn_server_time_zone(
+ cli->conn)));
+ finfo->ctime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->atime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->mtime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
+ finfo->size = IVAL(p,16);
+ finfo->attr = SVAL(p,24);
+ len = CVAL(p, 30);
+ p += 31;
+ /* check for unisys! */
+ if (p + len + 1 > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->name,
+ p,
+ len,
+ STR_NOALIGN);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += ret;
+ return PTR_DIFF(p, base) + 1;
+
+ case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
+ {
+ size_t namelen, slen;
+
+ if (pdata_end - base < 94) {
+ return pdata_end - base;
+ }
+
+ p += 4; /* next entry offset */
+
+ if (p_resume_key) {
+ *p_resume_key = IVAL(p,0);
+ }
+ p += 4; /* fileindex */
+
+ /* Offset zero is "create time", not "change time". */
+ p += 8;
+ finfo->atime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->mtime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->ctime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
+ p += 8;
+ p += 8; /* alloc size */
+ finfo->attr = IVAL(p,0);
+ p += 4;
+ namelen = IVAL(p,0);
+ p += 4;
+ p += 4; /* EA size */
+ slen = CVAL(p, 0);
+ if (slen > 24) {
+ /* Bad short name length. */
+ return pdata_end - base;
+ }
+ p += 2;
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->short_name,
+ p,
+ slen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += 24; /* short name? */
+ if (p + namelen < p || p + namelen > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->name,
+ p,
+ namelen,
+ 0);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+
+ /* To be robust in the face of unicode conversion failures
+ we need to copy the raw bytes of the last name seen here.
+ Namelen doesn't include the terminating unicode null, so
+ copy it here. */
+
+ if (p_last_name_raw) {
+ *p_last_name_raw = data_blob(NULL, namelen+2);
+ memcpy(p_last_name_raw->data, p, namelen);
+ SSVAL(p_last_name_raw->data, namelen, 0);
+ }
+ return calc_next_entry_offset(base, pdata_end);
+ }
+ }
+
+ DEBUG(1,("Unknown long filename format %d\n",level));
+ return calc_next_entry_offset(base, pdata_end);
+}
+
+/****************************************************************************
+ Interpret a short filename structure.
+ The length of the structure is returned.
+****************************************************************************/
+
+static bool interpret_short_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ char *p,
+ struct file_info *finfo)
+{
+ size_t ret;
+ ZERO_STRUCTP(finfo);
+
+ finfo->attr = CVAL(p,21);
+
+ /* We don't get birth time. */
+ finfo->btime_ts.tv_sec = 0;
+ finfo->btime_ts.tv_nsec = 0;
+ /* this date is converted to GMT by make_unix_date */
+ finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
+ finfo->ctime_ts.tv_nsec = 0;
+ finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
+ finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
+ finfo->size = IVAL(p,26);
+ ret = pull_string_talloc(ctx,
+ NULL,
+ 0,
+ &finfo->name,
+ p+30,
+ 12,
+ STR_ASCII);
+ if (ret == (size_t)-1) {
+ return false;
+ }
+
+ if (finfo->name) {
+ finfo->short_name = talloc_strdup(ctx, finfo->name);
+ if (finfo->short_name == NULL) {
+ return false;
+ }
+ }
+ return true;
+}
+
+struct cli_list_old_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t vwv[2];
+ char *mask;
+ int num_asked;
+ uint32_t attribute;
+ uint8_t search_status[23];
+ bool first;
+ bool done;
+ uint8_t *dirlist;
+};
+
+static void cli_list_old_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_list_old_state *state;
+ uint8_t *bytes;
+ static const uint16_t zero = 0;
+ uint32_t usable_space;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->attribute = attribute;
+ state->first = true;
+ state->mask = talloc_strdup(state, mask);
+ if (tevent_req_nomem(state->mask, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->mask = smb1_dfs_share_path(state, cli, state->mask);
+ if (tevent_req_nomem(state->mask, req)) {
+ return tevent_req_post(req, ev);
+ }
+ usable_space = cli_state_available_size(cli, 100);
+ state->num_asked = usable_space / DIR_STRUCT_SIZE;
+
+ SSVAL(state->vwv + 0, 0, state->num_asked);
+ SSVAL(state->vwv + 1, 0, state->attribute);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ state->mask,
+ strlen(state->mask)+1,
+ NULL);
+
+ bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
+ 2, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_list_old_done, req);
+ return req;
+}
+
+static void cli_list_old_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_list_old_state *state = tevent_req_data(
+ req, struct cli_list_old_state);
+ NTSTATUS status;
+ uint8_t cmd;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
+ uint16_t received;
+ size_t dirlist_len;
+ uint8_t *tmp;
+
+ status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
+ &bytes);
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
+ && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ TALLOC_FREE(subreq);
+ tevent_req_nterror(req, status);
+ return;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
+ || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ received = 0;
+ } else {
+ if (wct < 1) {
+ TALLOC_FREE(subreq);
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ received = SVAL(vwv + 0, 0);
+ }
+
+ if (received > 0) {
+ /*
+ * I don't think this can wrap. received is
+ * initialized from a 16-bit value.
+ */
+ if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) {
+ TALLOC_FREE(subreq);
+ tevent_req_nterror(
+ req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ dirlist_len = talloc_get_size(state->dirlist);
+
+ tmp = talloc_realloc(
+ state, state->dirlist, uint8_t,
+ dirlist_len + received * DIR_STRUCT_SIZE);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->dirlist = tmp;
+ memcpy(state->dirlist + dirlist_len, bytes + 3,
+ received * DIR_STRUCT_SIZE);
+
+ SSVAL(state->search_status, 0, 21);
+ memcpy(state->search_status + 2,
+ bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
+ cmd = SMBsearch;
+ } else {
+ if (state->first || state->done) {
+ tevent_req_done(req);
+ return;
+ }
+ state->done = true;
+ state->num_asked = 0;
+ cmd = SMBfclose;
+ }
+ TALLOC_FREE(subreq);
+
+ state->first = false;
+
+ SSVAL(state->vwv + 0, 0, state->num_asked);
+ SSVAL(state->vwv + 1, 0, state->attribute);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
+ 1, NULL);
+ bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
+ sizeof(state->search_status));
+ if (tevent_req_nomem(bytes, req)) {
+ return;
+ }
+ subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
+ 2, state->vwv, talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_list_old_done, req);
+}
+
+static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo)
+{
+ struct cli_list_old_state *state = tevent_req_data(
+ req, struct cli_list_old_state);
+ NTSTATUS status;
+ size_t i, num_received;
+ struct file_info *finfo;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->dirlist == NULL) {
+ *pfinfo = NULL;
+ return NT_STATUS_OK;
+ }
+
+ num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
+
+ finfo = talloc_array(mem_ctx, struct file_info, num_received);
+ if (finfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_received; i++) {
+ if (!interpret_short_filename(
+ finfo, state->cli,
+ (char *)state->dirlist + i * DIR_STRUCT_SIZE,
+ &finfo[i])) {
+ TALLOC_FREE(finfo);
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (finfo->name == NULL) {
+ TALLOC_FREE(finfo);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbXcli_conn_disconnect(state->cli->conn, status);
+ TALLOC_FREE(finfo);
+ return status;
+ }
+ }
+ TALLOC_FREE(state->dirlist);
+ *pfinfo = finfo;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
+ uint32_t attribute,
+ NTSTATUS (*fn)(struct file_info *,
+ const char *, void *), void *state)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ struct file_info *finfo = NULL;
+ size_t i, num_finfo;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_list_old_send(frame, ev, cli, mask, attribute);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_list_old_recv(req, frame, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ num_finfo = talloc_array_length(finfo);
+ for (i=0; i<num_finfo; i++) {
+ status = fn(&finfo[i], mask, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_list_trans_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ char *mask;
+ uint32_t attribute;
+ uint16_t info_level;
+
+ int loop_count;
+ int total_received;
+ uint16_t max_matches;
+ bool first;
+
+ int ff_eos;
+ int ff_dir_handle;
+
+ uint16_t setup[1];
+ uint8_t *param;
+
+ struct file_info *finfo;
+};
+
+static void cli_list_trans_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute,
+ uint16_t info_level)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_list_trans_state *state;
+ size_t param_len;
+ uint16_t additional_flags2 = 0;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_list_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->mask = talloc_strdup(state, mask);
+ if (tevent_req_nomem(state->mask, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->mask = smb1_dfs_share_path(state, cli, state->mask);
+ if (tevent_req_nomem(state->mask, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->attribute = attribute;
+ state->info_level = info_level;
+ state->loop_count = 0;
+ state->first = true;
+
+ state->max_matches = 1366; /* Match W2k */
+
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
+
+ state->param = talloc_array(state, uint8_t, 12);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SSVAL(state->param, 0, state->attribute);
+ SSVAL(state->param, 2, state->max_matches);
+ SSVAL(state->param, 4,
+ FLAG_TRANS2_FIND_REQUIRE_RESUME
+ |FLAG_TRANS2_FIND_CLOSE_IF_END
+ |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
+ SSVAL(state->param, 6, state->info_level);
+ SIVAL(state->param, 8, 0);
+
+ state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
+ state->mask, strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(state->mask)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ param_len = talloc_get_size(state->param);
+
+ subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
+ SMBtrans2, NULL, -1, 0, 0,
+ state->setup, 1, 0,
+ state->param, param_len, 10,
+ NULL, 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_list_trans_done, req);
+ return req;
+}
+
+static void cli_list_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_list_trans_state *state = tevent_req_data(
+ req, struct cli_list_trans_state);
+ NTSTATUS status;
+ uint8_t *param;
+ uint32_t num_param;
+ uint8_t *data;
+ char *data_end;
+ uint32_t num_data;
+ uint32_t min_param;
+ struct file_info *tmp;
+ size_t old_num_finfo;
+ uint16_t recv_flags2;
+ int ff_searchcount;
+ bool ff_eos;
+ char *p, *p2;
+ uint32_t resume_key = 0;
+ int i;
+ DATA_BLOB last_name_raw;
+ struct file_info *finfo = NULL;
+ size_t param_len;
+ uint16_t additional_flags2 = 0;
+
+ min_param = (state->first ? 6 : 4);
+
+ status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
+ NULL, 0, NULL,
+ &param, min_param, &num_param,
+ &data, 0, &num_data);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * TODO: retry, OS/2 nofiles
+ */
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (state->first) {
+ state->ff_dir_handle = SVAL(param, 0);
+ ff_searchcount = SVAL(param, 2);
+ ff_eos = SVAL(param, 4) != 0;
+ } else {
+ ff_searchcount = SVAL(param, 0);
+ ff_eos = SVAL(param, 2) != 0;
+ }
+
+ old_num_finfo = talloc_array_length(state->finfo);
+
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
+ old_num_finfo + ff_searchcount);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->finfo = tmp;
+
+ p2 = p = (char *)data;
+ data_end = (char *)data + num_data;
+ last_name_raw = data_blob_null;
+
+ for (i=0; i<ff_searchcount; i++) {
+ if (p2 >= data_end) {
+ ff_eos = true;
+ break;
+ }
+ if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
+ && (i == ff_searchcount-1)) {
+ /* Last entry - fixup the last offset length. */
+ SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
+ }
+
+ data_blob_free(&last_name_raw);
+
+ finfo = &state->finfo[old_num_finfo + i];
+
+ p2 += interpret_long_filename(
+ state->finfo, /* Stick fname to the array as such */
+ state->cli, state->info_level,
+ (char *)data, recv_flags2, p2,
+ data_end, finfo, &resume_key, &last_name_raw);
+
+ if (finfo->name == NULL) {
+ DEBUG(1, ("cli_list: Error: unable to parse name from "
+ "info level %d\n", state->info_level));
+ tevent_req_nterror(req,
+ NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbXcli_conn_disconnect(state->cli->conn, status);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if (!state->first && (state->mask[0] != '\0') &&
+ strcsequal(finfo->name, state->mask)) {
+ DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
+ "already been seen?\n", finfo->name));
+ ff_eos = true;
+ break;
+ }
+ }
+
+ if (ff_searchcount == 0) {
+ ff_eos = true;
+ }
+
+ TALLOC_FREE(param);
+ TALLOC_FREE(data);
+
+ /*
+ * Shrink state->finfo to the real length we received
+ */
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
+ old_num_finfo + i);
+ if (tevent_req_nomem(tmp, req)) {
+ return;
+ }
+ state->finfo = tmp;
+
+ state->first = false;
+
+ if (ff_eos) {
+ data_blob_free(&last_name_raw);
+ tevent_req_done(req);
+ return;
+ }
+
+ TALLOC_FREE(state->mask);
+ state->mask = talloc_strdup(state, finfo->name);
+ if (tevent_req_nomem(state->mask, req)) {
+ return;
+ }
+
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
+
+ param = talloc_realloc(state, state->param, uint8_t, 12);
+ if (tevent_req_nomem(param, req)) {
+ return;
+ }
+ state->param = param;
+
+ SSVAL(param, 0, state->ff_dir_handle);
+ SSVAL(param, 2, state->max_matches); /* max count */
+ SSVAL(param, 4, state->info_level);
+ /*
+ * For W2K servers serving out FAT filesystems we *must* set
+ * the resume key. If it's not FAT then it's returned as zero.
+ */
+ SIVAL(param, 6, resume_key); /* ff_resume_key */
+ /*
+ * NB. *DON'T* use continue here. If you do it seems that W2K
+ * and brethren can miss filenames. Use last filename
+ * continue instead. JRA
+ */
+ SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
+ |FLAG_TRANS2_FIND_CLOSE_IF_END
+ |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
+ if (last_name_raw.length) {
+ state->param = trans2_bytes_push_bytes(state->param,
+ last_name_raw.data,
+ last_name_raw.length);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
+ data_blob_free(&last_name_raw);
+ } else {
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(state->cli->conn),
+ state->mask,
+ strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
+ }
+ param_len = talloc_get_size(state->param);
+
+ if (clistr_is_previous_version_path(state->mask)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
+ SMBtrans2, NULL, -1, 0, 0,
+ state->setup, 1, 0,
+ state->param, param_len, 10,
+ NULL, 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_list_trans_done, req);
+}
+
+static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **finfo)
+{
+ struct cli_list_trans_state *state = tevent_req_data(
+ req, struct cli_list_trans_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *finfo = talloc_move(mem_ctx, &state->finfo);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
+ uint32_t attribute, int info_level,
+ NTSTATUS (*fn)(
+ struct file_info *finfo,
+ const char *mask,
+ void *private_data),
+ void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ int i, num_finfo;
+ struct file_info *finfo = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_list_trans_recv(req, frame, &finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ num_finfo = talloc_array_length(finfo);
+ for (i=0; i<num_finfo; i++) {
+ status = fn(&finfo[i], mask, private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_list_state {
+ struct tevent_context *ev;
+ struct tevent_req *subreq;
+ NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct file_info **finfo);
+ struct file_info *finfo;
+ size_t num_received;
+};
+
+static void cli_list_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute,
+ uint16_t info_level)
+{
+ struct tevent_req *req = NULL;
+ struct cli_list_state *state;
+ enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+
+ if (proto >= PROTOCOL_SMB2_02) {
+ state->subreq = cli_smb2_list_send(state, ev, cli, mask,
+ info_level);
+ state->recv_fn = cli_smb2_list_recv;
+ } else if (proto >= PROTOCOL_LANMAN2) {
+ state->subreq = cli_list_trans_send(
+ state, ev, cli, mask, attribute, info_level);
+ state->recv_fn = cli_list_trans_recv;
+ } else {
+ state->subreq = cli_list_old_send(
+ state, ev, cli, mask, attribute);
+ state->recv_fn = cli_list_old_recv;
+ }
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, cli_list_done, req);
+ return req;
+}
+
+static void cli_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_list_state *state = tevent_req_data(
+ req, struct cli_list_state);
+ NTSTATUS status;
+
+ SMB_ASSERT(subreq == state->subreq);
+
+ /*
+ * We don't want to be called by the lowerlevel routines
+ * from within state->recv_fn()
+ */
+ tevent_req_set_callback(subreq, NULL, NULL);
+
+ status = state->recv_fn(subreq, state, &state->finfo);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+ /* We'll get back here */
+ tevent_req_set_callback(subreq, cli_list_done, req);
+ return;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_notify_callback(req);
+}
+
+NTSTATUS cli_list_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo)
+{
+ struct cli_list_state *state = tevent_req_data(
+ req, struct cli_list_state);
+ size_t num_results;
+ struct file_info *finfo = NULL;
+ NTSTATUS status;
+ bool in_progress;
+
+ in_progress = tevent_req_is_in_progress(req);
+
+ if (!in_progress) {
+ if (!tevent_req_is_nterror(req, &status)) {
+ status = NT_STATUS_NO_MORE_FILES;
+ }
+ return status;
+ }
+
+ if (state->finfo == NULL) {
+ status = state->recv_fn(state->subreq, state, &state->finfo);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
+ tevent_req_set_callback(
+ state->subreq, cli_list_done, req);
+ return NT_STATUS_RETRY;
+ }
+
+ if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) {
+ status = NT_STATUS_NO_MORE_FILES;
+ }
+
+ if (tevent_req_nterror(req, status)) {
+ return status;
+ }
+
+ state->num_received = 0;
+ }
+
+ num_results = talloc_array_length(state->finfo);
+
+ if (num_results == 1) {
+ finfo = talloc_move(mem_ctx, &state->finfo);
+ } else {
+ struct file_info *src_finfo =
+ &state->finfo[state->num_received];
+
+ finfo = talloc(mem_ctx, struct file_info);
+ if (finfo == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *finfo = *src_finfo;
+ finfo->name = talloc_move(finfo, &src_finfo->name);
+ finfo->short_name = talloc_move(finfo, &src_finfo->short_name);
+ }
+
+ state->num_received += 1;
+
+ if (state->num_received == num_results) {
+ TALLOC_FREE(state->finfo);
+ }
+
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+
+ *pfinfo = finfo;
+ return NT_STATUS_OK;
+}
+
+struct cli_list_sync_state {
+ const char *mask;
+ uint32_t attribute;
+ NTSTATUS (*fn)(struct file_info *finfo,
+ const char *mask,
+ void *private_data);
+ void *private_data;
+ NTSTATUS status;
+ bool processed_file;
+};
+
+static void cli_list_sync_cb(struct tevent_req *subreq)
+{
+ struct cli_list_sync_state *state =
+ tevent_req_callback_data_void(subreq);
+ struct file_info *finfo;
+ bool ok;
+
+ state->status = cli_list_recv(subreq, talloc_tos(), &finfo);
+ /* No TALLOC_FREE(subreq), we get here more than once */
+
+ if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) {
+ /*
+ * The lowlevel SMB call was rearmed, we'll get back
+ * here when it's done.
+ */
+ state->status = NT_STATUS_OK;
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ return;
+ }
+
+ ok = dir_check_ftype(finfo->attr, state->attribute);
+ if (!ok) {
+ /*
+ * Only process if attributes match. On SMB1 server
+ * does this, so on SMB2 we need to emulate in the
+ * client.
+ *
+ * https://bugzilla.samba.org/show_bug.cgi?id=10260
+ */
+ return;
+ }
+
+ state->status = state->fn(finfo, state->mask, state->private_data);
+
+ state->processed_file = true;
+
+ TALLOC_FREE(finfo);
+}
+
+NTSTATUS cli_list(struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute,
+ NTSTATUS (*fn)(struct file_info *finfo,
+ const char *mask,
+ void *private_data),
+ void *private_data)
+{
+ TALLOC_CTX *frame = NULL;
+ struct cli_list_sync_state state = {
+ .mask = mask,
+ .attribute = attribute,
+ .fn = fn,
+ .private_data = private_data,
+ };
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ uint16_t info_level;
+ enum protocol_types proto = smbXcli_conn_protocol(cli->conn);
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ if (proto >= PROTOCOL_SMB2_02) {
+ info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+ } else {
+ info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
+ ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
+ }
+
+ req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
+ if (req == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(req, cli_list_sync_cb, &state);
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = state.status;
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
+ status = NT_STATUS_OK;
+ }
+
+ if (NT_STATUS_IS_OK(status) && !state.processed_file) {
+ status = NT_STATUS_NO_SUCH_FILE;
+ }
+
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/climessage.c b/source3/libsmb/climessage.c
new file mode 100644
index 0000000..8563615
--- /dev/null
+++ b/source3/libsmb/climessage.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+ client message handling routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+struct cli_message_start_state {
+ uint16_t grp;
+};
+
+static void cli_message_start_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *host,
+ const char *username)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_message_start_state *state;
+ char *htmp = NULL;
+ char *utmp = NULL;
+ size_t hlen, ulen;
+ uint8_t *bytes, *p;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_message_start_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+ username, strlen(username)+1,
+ &utmp, &ulen)) {
+ goto fail;
+ }
+ if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+ host, strlen(host)+1,
+ &htmp, &hlen)) {
+ goto fail;
+ }
+
+ bytes = talloc_array(state, uint8_t, ulen+hlen+2);
+ if (bytes == NULL) {
+ goto fail;
+ }
+ p = bytes;
+
+ *p++ = 4;
+ memcpy(p, utmp, ulen);
+ p += ulen;
+ *p++ = 4;
+ memcpy(p, htmp, hlen);
+ TALLOC_FREE(htmp);
+ TALLOC_FREE(utmp);
+
+ subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, 0, NULL,
+ talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_message_start_done, req);
+ return req;
+fail:
+ TALLOC_FREE(htmp);
+ TALLOC_FREE(utmp);
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+}
+
+static void cli_message_start_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_message_start_state *state = tevent_req_data(
+ req, struct cli_message_start_state);
+ NTSTATUS status;
+ uint8_t wct;
+ uint16_t *vwv;
+
+ status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv,
+ NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (wct >= 1) {
+ state->grp = SVAL(vwv+0, 0);
+ } else {
+ state->grp = 0;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_message_start_recv(struct tevent_req *req,
+ uint16_t *pgrp)
+{
+ struct cli_message_start_state *state = tevent_req_data(
+ req, struct cli_message_start_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pgrp = state->grp;
+ return NT_STATUS_OK;
+}
+
+struct cli_message_text_state {
+ uint16_t vwv;
+};
+
+static void cli_message_text_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t grp,
+ const char *msg,
+ int msglen)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_message_text_state *state;
+ char *tmp;
+ size_t tmplen;
+ uint8_t *bytes;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_message_text_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(&state->vwv, 0, grp);
+
+ if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
+ &tmp, &tmplen)) {
+ msg = tmp;
+ msglen = tmplen;
+ } else {
+ DEBUG(3, ("Conversion failed, sending message in UNIX "
+ "charset\n"));
+ tmp = NULL;
+ }
+
+ bytes = talloc_array(state, uint8_t, msglen+3);
+ if (tevent_req_nomem(bytes, req)) {
+ TALLOC_FREE(tmp);
+ return tevent_req_post(req, ev);
+ }
+ SCVAL(bytes, 0, 1); /* pad */
+ SSVAL(bytes+1, 0, msglen);
+ memcpy(bytes+3, msg, msglen);
+ TALLOC_FREE(tmp);
+
+ subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 0, 1, &state->vwv,
+ talloc_get_size(bytes), bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_message_text_done, req);
+ return req;
+}
+
+static void cli_message_text_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_message_text_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_message_end_state {
+ uint16_t vwv;
+};
+
+static void cli_message_end_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t grp)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_message_end_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_message_end_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ SSVAL(&state->vwv, 0, grp);
+
+ subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 0, 1, &state->vwv,
+ 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_message_end_done, req);
+ return req;
+}
+
+static void cli_message_end_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_message_end_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_message_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ size_t sent;
+ const char *message;
+ uint16_t grp;
+};
+
+static void cli_message_started(struct tevent_req *subreq);
+static void cli_message_sent(struct tevent_req *subreq);
+static void cli_message_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *host, const char *username,
+ const char *message)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_message_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->sent = 0;
+ state->message = message;
+
+ subreq = cli_message_start_send(state, ev, cli, host, username);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_message_started, req);
+ return req;
+}
+
+static void cli_message_started(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_message_state *state = tevent_req_data(
+ req, struct cli_message_state);
+ NTSTATUS status;
+ size_t thistime;
+
+ status = cli_message_start_recv(subreq, &state->grp);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ thistime = MIN(127, strlen(state->message));
+
+ subreq = cli_message_text_send(state, state->ev, state->cli,
+ state->grp, state->message, thistime);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ state->sent += thistime;
+ tevent_req_set_callback(subreq, cli_message_sent, req);
+}
+
+static void cli_message_sent(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_message_state *state = tevent_req_data(
+ req, struct cli_message_state);
+ NTSTATUS status;
+ size_t left, thistime;
+
+ status = cli_message_text_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->sent >= strlen(state->message)) {
+ subreq = cli_message_end_send(state, state->ev, state->cli,
+ state->grp);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_message_done, req);
+ return;
+ }
+
+ left = strlen(state->message) - state->sent;
+ thistime = MIN(127, left);
+
+ subreq = cli_message_text_send(state, state->ev, state->cli,
+ state->grp,
+ state->message + state->sent,
+ thistime);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ state->sent += thistime;
+ tevent_req_set_callback(subreq, cli_message_sent, req);
+}
+
+static void cli_message_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+
+ status = cli_message_end_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_message_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_message(struct cli_state *cli, const char *host,
+ const char *username, const char *message)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_message_send(frame, ev, cli, host, username, message);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_message_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clioplock.c b/source3/libsmb/clioplock.c
new file mode 100644
index 0000000..aee09da
--- /dev/null
+++ b/source3/libsmb/clioplock.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client oplock functions
+ Copyright (C) Andrew Tridgell 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+struct cli_smb_oplock_break_waiter_state {
+ uint16_t fnum;
+ uint8_t level;
+};
+
+static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb_oplock_break_waiter_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb_oplock_break_waiter_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Create a fake SMB request that we will never send out. This is only
+ * used to be set into the pending queue with the right mid.
+ */
+ subreq = smb1cli_req_create(mem_ctx, ev, cli->conn, 0, 0, 0, 0, 0, 0,
+ 0, NULL, NULL, 0, NULL, 0, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ smb1cli_req_set_mid(subreq, 0xffff);
+
+ if (!smbXcli_req_set_pending(subreq)) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb_oplock_break_waiter_done, req);
+ return req;
+}
+
+static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
+ req, struct cli_smb_oplock_break_waiter_state);
+ struct iovec *iov;
+ uint8_t wct;
+ uint16_t *vwv;
+ NTSTATUS status;
+
+ status = smb1cli_req_recv(subreq, state,
+ &iov, /* piov */
+ NULL, /* phdr */
+ &wct,
+ &vwv,
+ NULL, /* pvwv_offset */
+ NULL, /* pnum_bytes */
+ NULL, /* pbytes */
+ NULL, /* pbytes_offset */
+ NULL, /* pinbuf */
+ NULL, 0); /* expected */
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (wct < 8) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ state->fnum = SVAL(vwv+2, 0);
+ state->level = CVAL(vwv+3, 1);
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req,
+ uint16_t *pfnum,
+ uint8_t *plevel)
+{
+ struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
+ req, struct cli_smb_oplock_break_waiter_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ *plevel = state->level;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+send an ack for an oplock break request
+****************************************************************************/
+
+struct cli_oplock_ack_state {
+ uint8_t dummy;
+};
+
+static void cli_oplock_ack_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_oplock_ack_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, uint8_t level)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_oplock_ack_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_oplock_ack_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ subreq = cli_lockingx_send(
+ state, /* mem_ctx */
+ ev, /* tevent_context */
+ cli, /* cli */
+ fnum, /* fnum */
+ LOCKING_ANDX_OPLOCK_RELEASE, /* typeoflock */
+ level, /* newoplocklevel */
+ 0, /* timeout */
+ 0, /* num_unlocks */
+ NULL, /* unlocks */
+ 0, /* num_locks */
+ NULL); /* locks */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_oplock_ack_done, req);
+ return req;
+}
+
+static void cli_oplock_ack_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_lockingx_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_oplock_ack_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
diff --git a/source3/libsmb/cliprint.c b/source3/libsmb/cliprint.c
new file mode 100644
index 0000000..b354990
--- /dev/null
+++ b/source3/libsmb/cliprint.c
@@ -0,0 +1,219 @@
+/*
+ Unix SMB/CIFS implementation.
+ client print routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/util/string_wrappers.h"
+
+/*****************************************************************************
+ Convert a character pointer in a cli_call_api() response to a form we can use.
+ This function contains code to prevent core dumps if the server returns
+ invalid data.
+*****************************************************************************/
+static const char *fix_char_ptr(unsigned int datap, unsigned int converter,
+ char *rdata, int rdrcnt)
+{
+ unsigned int offset;
+
+ if (datap == 0) {
+ /* turn NULL pointers into zero length strings */
+ return "";
+ }
+
+ offset = datap - converter;
+
+ if (offset >= rdrcnt) {
+ DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>\n",
+ datap, converter, rdrcnt));
+ return "<ERROR>";
+ }
+ return &rdata[offset];
+}
+
+/****************************************************************************
+call fn() on each entry in a print queue
+****************************************************************************/
+
+NTSTATUS cli_print_queue(struct cli_state *cli,
+ void (*fn)(struct print_job_info *))
+{
+ uint8_t *rparam = NULL;
+ uint8_t *rdata = NULL;
+ char *p = NULL;
+ uint32_t rdrcnt, rprcnt;
+ char param[1024];
+ int converter;
+ int result_code=0;
+ int i = -1;
+ NTSTATUS status;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,76); /* API function number 76 (DosPrintJobEnum) */
+ p += 2;
+ strlcpy_base(p,"zWrLeh", param, sizeof(param)); /* parameter description? */
+ p = skip_string(param,sizeof(param),p);
+ strlcpy_base(p,"WWzWWDDzz", param, sizeof(param)); /* returned data format */
+ p = skip_string(param,sizeof(param),p);
+ strlcpy_base(p,cli->share, param, sizeof(param)); /* name of queue */
+ p = skip_string(param,sizeof(param),p);
+ SSVAL(p,0,2); /* API function level 2, PRJINFO_2 data structure */
+ SSVAL(p,2,1000); /* size of bytes of returned data buffer */
+ p += 4;
+ strlcpy_base(p,"", param,sizeof(param)); /* subformat */
+ p = skip_string(param,sizeof(param),p);
+
+ DEBUG(4,("doing cli_print_queue for %s\n", cli->share));
+
+ status = cli_trans(
+ talloc_tos(),
+ cli,
+ SMBtrans, /* trans_cmd */
+ "\\PIPE\\LANMAN", /* name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ (uint8_t *)param, /* param */
+ PTR_DIFF(p,param), /* num_param */
+ 1024, /* max_param */
+ NULL, /* data */
+ 0, /* num_data */
+ CLI_BUFFER_SIZE, /* max_data */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min_rsetup */
+ NULL, /* num_rsetup */
+ &rparam, /* rparam */
+ 8, /* min_rparam */
+ &rprcnt, /* num_rparam */
+ &rdata, /* rdata */
+ 0, /* min_rdata */
+ &rdrcnt); /* num_rdata */
+ if (!NT_STATUS_IS_OK(status)) {
+ cli->raw_status = status;
+ return status;
+ }
+
+ result_code = SVAL(rparam,0);
+ converter = SVAL(rparam,2); /* conversion factor */
+
+ if (result_code == 0) {
+ struct print_job_info job;
+
+ p = (char *)rdata;
+
+ for (i = 0; i < SVAL(rparam,4); ++i) {
+ job.id = SVAL(p,0);
+ job.priority = SVAL(p,2);
+ fstrcpy(job.user,
+ fix_char_ptr(SVAL(p,4), converter,
+ (char *)rdata, rdrcnt));
+ job.t = make_unix_date3(
+ p + 12, smb1cli_conn_server_time_zone(cli->conn));
+ job.size = IVAL(p,16);
+ fstrcpy(job.name,fix_char_ptr(SVAL(p,24),
+ converter,
+ (char *)rdata, rdrcnt));
+ fn(&job);
+ p += 28;
+ }
+ }
+
+ /* If any parameters or data were returned, free the storage. */
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ cancel a print job
+ ****************************************************************************/
+
+NTSTATUS cli_printjob_del(struct cli_state *cli, int job)
+{
+ uint8_t *rparam = NULL;
+ uint8_t *rdata = NULL;
+ char *p = NULL;
+ uint32_t rdrcnt, rprcnt;
+ int result_code;
+ char param[1024];
+ NTSTATUS status = NT_STATUS_OK;
+
+ memset(param,'\0',sizeof(param));
+
+ p = param;
+ SSVAL(p,0,81); /* DosPrintJobDel() */
+ p += 2;
+ strlcpy_base(p,"W", param,sizeof(param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy_base(p,"", param,sizeof(param));
+ p = skip_string(param,sizeof(param),p);
+ SSVAL(p,0,job);
+ p += 2;
+
+ status = cli_trans(talloc_tos(),
+ cli,
+ SMBtrans, /* trans_cmd */
+ "\\PIPE\\LANMAN", /* name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ (uint8_t *)param, /* param */
+ PTR_DIFF(p, param), /* num_param */
+ 1024, /* max_param */
+ NULL, /* data */
+ 0, /* num_data */
+ CLI_BUFFER_SIZE, /* max_data */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min_rsetup */
+ NULL, /* num_rsetup */
+ &rparam, /* rparam */
+ 8, /* min_rparam */
+ &rprcnt, /* num_rparam */
+ &rdata, /* rdata */
+ 0, /* min_rdata */
+ &rdrcnt); /* num_rdata */
+ if (!NT_STATUS_IS_OK(status)) {
+ cli->raw_status = status;
+ return status;
+ }
+
+ result_code = SVAL(rparam, 0);
+
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+
+ if (result_code == ERRnosuchprintjob) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ cli->raw_status = NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return status;
+}
diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
new file mode 100644
index 0000000..0c41272
--- /dev/null
+++ b/source3/libsmb/cliquota.c
@@ -0,0 +1,696 @@
+/*
+ Unix SMB/CIFS implementation.
+ client quota functions
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "fake_file.h"
+#include "../libcli/security/security.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+
+NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
+{
+ return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32,
+ 0x00000016, DESIRED_ACCESS_PIPE,
+ 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x00000000, 0x03, quota_fnum, NULL);
+}
+
+void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
+{
+ if (!qt_list || !*qt_list) {
+ return;
+ }
+
+ if ((*qt_list)->mem_ctx)
+ talloc_destroy((*qt_list)->mem_ctx);
+
+ (*qt_list) = NULL;
+
+ return;
+}
+
+bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_STRUCT *pqt,
+ SMB_NTQUOTA_LIST **pqt_list)
+{
+ SMB_NTQUOTA_LIST *tmp_list_ent;
+
+ if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) == NULL) {
+ return false;
+ }
+
+ if ((tmp_list_ent->quotas = talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) ==
+ NULL) {
+ return false;
+ }
+
+ *tmp_list_ent->quotas = *pqt;
+ tmp_list_ent->mem_ctx = mem_ctx;
+
+ DLIST_ADD((*pqt_list), tmp_list_ent);
+
+ return true;
+}
+
+bool parse_user_quota_record(const uint8_t *rdata,
+ unsigned int rdata_count,
+ unsigned int *offset,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ struct file_quota_information info = {0};
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB blob;
+ enum ndr_err_code err;
+ bool result = false;
+
+ blob.data = discard_const_p(uint8_t, rdata);
+ blob.length = rdata_count;
+ err = ndr_pull_struct_blob(
+ &blob,
+ frame,
+ &info,
+ (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ goto out;
+ }
+
+ *offset = info.next_entry_offset;
+
+ ZERO_STRUCTP(pqt);
+ pqt->usedspace = info.quota_used;
+
+ pqt->softlim = info.quota_threshold;
+
+ pqt->hardlim = info.quota_limit;
+
+ pqt->qtype = SMB_USER_QUOTA_TYPE;
+ pqt->sid = info.sid;
+ result = true;
+out:
+ TALLOC_FREE(frame);
+ return result;
+}
+
+NTSTATUS parse_user_quota_list(const uint8_t *curdata,
+ uint32_t curdata_count,
+ TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST **pqt_list)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ unsigned offset;
+ SMB_NTQUOTA_STRUCT qt;
+
+ while (true) {
+ ZERO_STRUCT(qt);
+ if (!parse_user_quota_record(curdata, curdata_count, &offset,
+ &qt)) {
+ DEBUG(1, ("Failed to parse the quota record\n"));
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ break;
+ }
+
+ if (offset > curdata_count) {
+ DEBUG(1, ("out of bounds offset in quota record\n"));
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ break;
+ }
+
+ if (curdata + offset < curdata) {
+ DEBUG(1, ("Pointer overflow in quota record\n"));
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ break;
+ }
+
+ if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) {
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ }
+
+ curdata += offset;
+ curdata_count -= offset;
+
+ if (offset == 0) {
+ break;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
+ unsigned int rdata_count,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ SMB_NTQUOTA_STRUCT qt;
+
+ ZERO_STRUCT(qt);
+
+ if (rdata_count < 48) {
+ /* minimum length is not enforced by SMB2 client.
+ */
+ DEBUG(1, ("small returned fs quota buffer\n"));
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ /* unknown_1 24 NULL bytes in pdata*/
+
+ /* the soft quotas 8 bytes (uint64_t)*/
+ qt.softlim = BVAL(rdata, 24);
+
+ /* the hard quotas 8 bytes (uint64_t)*/
+ qt.hardlim = BVAL(rdata, 32);
+
+ /* quota_flags 2 bytes **/
+ qt.qflags = SVAL(rdata, 40);
+
+ qt.qtype = SMB_USER_FS_QUOTA_TYPE;
+
+ *pqt = qt;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
+ uint32_t maxlen,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *outbuf,
+ SMB_NTQUOTA_LIST **end_ptr)
+{
+ return fill_quota_buffer(mem_ctx,
+ qt_list,
+ false,
+ maxlen,
+ outbuf,
+ end_ptr);
+}
+
+NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
+ const SMB_NTQUOTA_STRUCT *pqt,
+ DATA_BLOB *blob,
+ uint32_t maxlen)
+{
+ uint8_t *buf;
+
+ if (maxlen > 0 && maxlen < 48) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ *blob = data_blob_talloc_zero(mem_ctx, 48);
+
+ if (!blob->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ buf = blob->data;
+
+ /* Unknown1 24 NULL bytes*/
+ SBIG_UINT(buf, 0, (uint64_t)0);
+ SBIG_UINT(buf, 8, (uint64_t)0);
+ SBIG_UINT(buf, 16, (uint64_t)0);
+
+ /* Default Soft Quota 8 bytes */
+ SBIG_UINT(buf, 24, pqt->softlim);
+
+ /* Default Hard Quota 8 bytes */
+ SBIG_UINT(buf, 32, pqt->hardlim);
+
+ /* Quota flag 4 bytes */
+ SIVAL(buf, 40, pqt->qflags);
+
+ /* 4 padding bytes */
+ SIVAL(buf, 44, 0);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint16_t setup[1];
+ uint8_t *rparam = NULL, *rdata = NULL;
+ uint32_t rparam_count, rdata_count;
+ unsigned int sid_len;
+ unsigned int offset;
+ struct nttrans_query_quota_params get_quota = {0};
+ struct file_get_quota_info info = {0};
+ enum ndr_err_code err;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB data_blob = data_blob_null;
+ DATA_BLOB param_blob = data_blob_null;
+
+ if (!cli||!pqt) {
+ smb_panic("cli_get_user_quota() called with NULL Pointer!");
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ TALLOC_FREE(frame);
+ return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
+ }
+
+ get_quota.fid = quota_fnum;
+ get_quota.return_single_entry = 1;
+ get_quota.restart_scan = 0;
+
+ sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+
+ info.next_entry_offset = 0;
+ info.sid_length = sid_len;
+ info.sid = pqt->sid;
+
+ err = ndr_push_struct_blob(
+ &data_blob,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ get_quota.sid_list_length = data_blob.length;
+ get_quota.start_sid_offset = data_blob.length;
+
+ err = ndr_push_struct_blob(
+ &param_blob,
+ frame,
+ &get_quota,
+ (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_GET_USER_QUOTA, 0,
+ setup, 1, 0, /* setup */
+ param_blob.data, param_blob.length, 4, /* params */
+ data_blob.data, data_blob.length, 112, /* data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 4, &rparam_count,
+ &rdata, 8, &rdata_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
+ nt_errstr(status)));
+ goto out;
+ }
+
+ if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
+ }
+
+out:
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS
+cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
+{
+ uint16_t setup[1];
+ uint8_t params[2];
+ DATA_BLOB data = data_blob_null;
+ NTSTATUS status;
+
+ if (!cli || !qtl) {
+ smb_panic("cli_set_user_quota() called with NULL Pointer!");
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
+ }
+
+ status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
+ * this status.
+ */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ goto cleanup;
+ }
+ }
+
+ SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
+
+ SSVAL(params,0,quota_fnum);
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_SET_USER_QUOTA, 0,
+ setup, 1, 0, /* setup */
+ params, 2, 0, /* params */
+ data.data, data.length, 0, /* data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparams */
+ NULL, 0, NULL); /* rdata */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n",
+ nt_errstr(status)));
+ }
+
+cleanup:
+ data_blob_free(&data);
+ return status;
+}
+
+static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list,
+ bool first)
+{
+ uint16_t setup[1];
+ DATA_BLOB params_blob = data_blob_null;
+ uint8_t *rparam=NULL, *rdata=NULL;
+ uint32_t rparam_count=0, rdata_count=0;
+ NTSTATUS status;
+ struct nttrans_query_quota_params quota_params = {0};
+ enum ndr_err_code err;
+
+ TALLOC_CTX *frame = NULL;
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
+ pqt_list, first);
+ }
+ frame = talloc_stackframe();
+
+ SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
+
+ quota_params.fid = quota_fnum;
+ if (first) {
+ quota_params.restart_scan = 1;
+ }
+ err = ndr_push_struct_blob(
+ &params_blob,
+ frame,
+ &quota_params,
+ (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_GET_USER_QUOTA, 0,
+ setup, 1, 0, /* setup */
+ params_blob.data, params_blob.length, 4, /* params */
+ NULL, 0, 2048, /* data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ &rparam, 0, &rparam_count,
+ &rdata, 0, &rdata_count);
+
+ /* compat. with smbd + safeguard against
+ * endless loop
+ */
+ if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup;
+ }
+
+ status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
+
+cleanup:
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+NTSTATUS cli_list_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = NULL;
+ bool first = true;
+
+ if (!cli || !pqt_list) {
+ smb_panic("cli_list_user_quota() called with NULL Pointer!");
+ }
+
+ *pqt_list = NULL;
+
+ if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ do {
+ status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
+ pqt_list, first);
+ first = false;
+ } while (NT_STATUS_IS_OK(status));
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ status = NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
+ TALLOC_FREE(mem_ctx);
+ }
+
+ return status;
+}
+
+NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint16_t setup[1];
+ uint8_t param[2];
+ uint8_t *rdata=NULL;
+ uint32_t rdata_count=0;
+ NTSTATUS status;
+
+ if (!cli||!pqt) {
+ smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
+ }
+
+ SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
+
+ SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, -1, /* name, fid */
+ 0, 0, /* function, flags */
+ setup, 1, 0, /* setup */
+ param, 2, 0, /* param */
+ NULL, 0, 560, /* data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ &rdata, 48, &rdata_count);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
+
+ TALLOC_FREE(rdata);
+ return status;
+}
+
+NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint16_t setup[1];
+ uint8_t param[4];
+ DATA_BLOB data = data_blob_null;
+ NTSTATUS status;
+
+ if (!cli||!pqt) {
+ smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_set_fs_quota_info(cli, quota_fnum, pqt);
+ }
+
+ status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
+
+ SSVAL(param,0,quota_fnum);
+ SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, -1, /* name, fid */
+ 0, 0, /* function, flags */
+ setup, 1, 0, /* setup */
+ param, 4, 0, /* param */
+ data.data, data.length, 0, /* data */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ NULL, 0, NULL, /* rparam */
+ NULL, 0, NULL); /* rdata */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
+ nt_errstr(status)));
+ }
+
+ return status;
+}
+
+NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST *qlist,
+ bool return_single,
+ uint32_t max_data,
+ DATA_BLOB *blob,
+ SMB_NTQUOTA_LIST **end_ptr)
+{
+ ndr_flags_type ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ struct ndr_push *qndr = NULL;
+ uint32_t start_offset = 0;
+ uint32_t padding = 0;
+ if (qlist == NULL) {
+ /* We must push at least one. */
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+
+ qndr = ndr_push_init_ctx(mem_ctx);
+ if (qndr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (;qlist != NULL; qlist = qlist->next) {
+ struct file_quota_information info = {0};
+ enum ndr_err_code err;
+ uint32_t dsize = sizeof(info.next_entry_offset)
+ + sizeof(info.sid_length)
+ + sizeof(info.change_time)
+ + sizeof(info.quota_used)
+ + sizeof(info.quota_threshold)
+ + sizeof(info.quota_limit);
+
+
+ info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0);
+
+ if (max_data) {
+ uint32_t curr_pos_no_padding = qndr->offset - padding;
+ uint32_t payload = dsize + info.sid_length;
+ uint32_t new_pos = (curr_pos_no_padding + payload);
+ if (new_pos < curr_pos_no_padding) {
+ /* Detect unlikely integer wrap */
+ DBG_ERR("Integer wrap while adjusting pos "
+ "0x%x by offset 0x%x\n",
+ curr_pos_no_padding, payload);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (new_pos > max_data) {
+ DBG_WARNING("Max data will be exceeded "
+ "writing next query info. "
+ "cur_pos 0x%x, sid_length 0x%x, "
+ "dsize 0x%x, max_data 0x%x\n",
+ curr_pos_no_padding,
+ info.sid_length,
+ dsize,
+ max_data);
+ break;
+ }
+ }
+
+ start_offset = qndr->offset;
+ info.sid = qlist->quotas->sid;
+ info.quota_used = qlist->quotas->usedspace;
+ info.quota_threshold = qlist->quotas->softlim;
+ info.quota_limit = qlist->quotas->hardlim;
+
+ err = ndr_push_file_quota_information(qndr,
+ ndr_flags,
+ &info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_DEBUG("Failed to push the quota sid\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* pidl will align to 8 bytes due to 8 byte members*/
+ /* Remember how much align padding we've used. */
+ padding = qndr->offset;
+
+ err = ndr_push_align(qndr, 8);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_DEBUG("ndr_push_align returned %s\n",
+ ndr_map_error2string(err));
+ return ndr_map_error2ntstatus(err);
+ }
+
+ padding = qndr->offset - padding;
+
+ /*
+ * Overwrite next_entry_offset for this entry now
+ * we know what it should be. We know we're using
+ * LIBNDR_FLAG_LITTLE_ENDIAN here so we can use
+ * SIVAL.
+ */
+ info.next_entry_offset = qndr->offset - start_offset;
+ SIVAL(qndr->data, start_offset, info.next_entry_offset);
+
+ if (return_single) {
+ break;
+ }
+ }
+
+ if (end_ptr != NULL) {
+ *end_ptr = qlist;
+ }
+
+ /* Remove the padding alignment on the last element pushed. */
+ blob->length = qndr->offset - padding;
+ blob->data = qndr->data;
+
+ /*
+ * Terminate the pushed array by setting next_entry_offset
+ * for the last element to zero.
+ */
+ if (blob->length >= sizeof(uint32_t)) {
+ SIVAL(qndr->data, start_offset, 0);
+ }
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
new file mode 100644
index 0000000..21ee346
--- /dev/null
+++ b/source3/libsmb/clirap.c
@@ -0,0 +1,1853 @@
+/*
+ Unix SMB/CIFS implementation.
+ client RAP calls
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) James Peach 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/smb/reparse.h"
+#include "cli_smb2_fnum.h"
+#include "lib/util/string_wrappers.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#define PIPE_LANMAN "\\PIPE\\LANMAN"
+
+/****************************************************************************
+ Call a remote api
+****************************************************************************/
+
+bool cli_api(struct cli_state *cli,
+ char *param, int prcnt, int mprcnt,
+ char *data, int drcnt, int mdrcnt,
+ char **rparam, unsigned int *rprcnt,
+ char **rdata, unsigned int *rdrcnt)
+{
+ NTSTATUS status;
+
+ uint8_t *my_rparam, *my_rdata;
+ uint32_t num_my_rparam, num_my_rdata;
+
+ status = cli_trans(talloc_tos(), cli, SMBtrans,
+ PIPE_LANMAN, 0, /* name, fid */
+ 0, 0, /* function, flags */
+ NULL, 0, 0, /* setup */
+ (uint8_t *)param, prcnt, mprcnt, /* Params, length, max */
+ (uint8_t *)data, drcnt, mdrcnt, /* Data, length, max */
+ NULL, /* recv_flags2 */
+ NULL, 0, NULL, /* rsetup */
+ &my_rparam, 0, &num_my_rparam,
+ &my_rdata, 0, &num_my_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ /*
+ * I know this memcpy massively hurts, but there are just tons
+ * of callers of cli_api that eventually need changing to
+ * talloc
+ */
+
+ *rparam = (char *)smb_memdup(my_rparam, num_my_rparam);
+ if (*rparam == NULL) {
+ goto fail;
+ }
+ *rprcnt = num_my_rparam;
+ TALLOC_FREE(my_rparam);
+
+ *rdata = (char *)smb_memdup(my_rdata, num_my_rdata);
+ if (*rdata == NULL) {
+ goto fail;
+ }
+ *rdrcnt = num_my_rdata;
+ TALLOC_FREE(my_rdata);
+
+ return true;
+fail:
+ TALLOC_FREE(my_rdata);
+ TALLOC_FREE(my_rparam);
+ *rparam = NULL;
+ *rprcnt = 0;
+ *rdata = NULL;
+ *rdrcnt = 0;
+ return false;
+}
+
+/****************************************************************************
+ Call a NetShareEnum - try and browse available connections on a host.
+****************************************************************************/
+
+int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32_t, const char *, void *), void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[1024];
+ int count = -1;
+ bool ok;
+ int res;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = param;
+ SSVAL(p,0,0); /* api number */
+ p += 2;
+ strlcpy(p,"WrLeh",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p,"B13BWz",sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ SSVAL(p,0,1);
+ /*
+ * Win2k needs a *smaller* buffer than 0xFFFF here -
+ * it returns "out of server memory" with 0xFFFF !!! JRA.
+ */
+ SSVAL(p,2,0xFFE0);
+ p += 4;
+
+ ok = cli_api(
+ cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFFE0, /* data, length, maxlen - Win2k needs a small buffer here too ! */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt); /* return data, length */
+ if (!ok) {
+ DEBUG(4,("NetShareEnum failed\n"));
+ goto done;
+ }
+
+ if (rprcnt < 6) {
+ DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt);
+ goto done;
+ }
+
+ res = rparam? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata) {
+ int converter=SVAL(rparam,2);
+ int i;
+ char *rdata_end = rdata + rdrcnt;
+
+ count=SVAL(rparam,4);
+ p = rdata;
+
+ for (i=0;i<count;i++,p+=20) {
+ char *sname;
+ int type;
+ int comment_offset;
+ const char *cmnt;
+ const char *p1;
+ char *s1, *s2;
+ size_t len;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (p + 20 > rdata_end) {
+ TALLOC_FREE(frame);
+ break;
+ }
+
+ sname = p;
+ type = SVAL(p,14);
+ comment_offset = (IVAL(p,16) & 0xFFFF) - converter;
+ if (comment_offset < 0 ||
+ comment_offset > (int)rdrcnt) {
+ TALLOC_FREE(frame);
+ break;
+ }
+ cmnt = comment_offset?(rdata+comment_offset):"";
+
+ /* Work out the comment length. */
+ for (p1 = cmnt, len = 0; *p1 &&
+ p1 < rdata_end; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+ pull_string_talloc(frame,rdata,0,
+ &s1,sname,14,STR_ASCII);
+ pull_string_talloc(frame,rdata,0,
+ &s2,cmnt,len,STR_ASCII);
+ if (!s1 || !s2) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ fn(s1, type, s2, state);
+
+ TALLOC_FREE(frame);
+ }
+ } else {
+ DEBUG(4,("NetShareEnum res=%d\n", res));
+ }
+
+done:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+/****************************************************************************
+ Call a NetServerEnum for the specified workgroup and servertype mask. This
+ function then calls the specified callback function for each name returned.
+
+ The callback function takes 4 arguments: the machine name, the server type,
+ the comment and a state pointer.
+****************************************************************************/
+
+bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32_t stype,
+ void (*fn)(const char *, uint32_t, const char *, void *),
+ void *state)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *rdata_end = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[1024];
+ int uLevel = 1;
+ size_t len;
+ uint32_t func = RAP_NetServerEnum2;
+ char *last_entry = NULL;
+ int total_cnt = 0;
+ int return_cnt = 0;
+ int res;
+
+ errno = 0; /* reset */
+
+ /*
+ * This may take more than one transaction, so we should loop until
+ * we no longer get a more data to process or we have all of the
+ * items.
+ */
+ do {
+ /* send a SMBtrans command with api NetServerEnum */
+ p = param;
+ SIVAL(p,0,func); /* api number */
+ p += 2;
+
+ if (func == RAP_NetServerEnum3) {
+ strlcpy(p,"WrLehDzz", sizeof(param)-PTR_DIFF(p,param));
+ } else {
+ strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
+ }
+
+ p = skip_string(param, sizeof(param), p);
+ strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+
+ p = skip_string(param, sizeof(param), p);
+ SSVAL(p,0,uLevel);
+ SSVAL(p,2,CLI_BUFFER_SIZE);
+ p += 4;
+ SIVAL(p,0,stype);
+ p += 4;
+
+ /* If we have more data, tell the server where
+ * to continue from.
+ */
+ len = push_ascii(p,
+ workgroup,
+ sizeof(param) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE|STR_UPPER);
+
+ if (len == 0) {
+ SAFE_FREE(last_entry);
+ return false;
+ }
+ p += len;
+
+ if (func == RAP_NetServerEnum3) {
+ len = push_ascii(p,
+ last_entry ? last_entry : "",
+ sizeof(param) - PTR_DIFF(p,param) - 1,
+ STR_TERMINATE);
+
+ if (len == 0) {
+ SAFE_FREE(last_entry);
+ return false;
+ }
+ p += len;
+ }
+
+ /* Next time through we need to use the continue api */
+ func = RAP_NetServerEnum3;
+
+ if (!cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt)) { /* return data, return size */
+
+ /* break out of the loop on error */
+ res = -1;
+ break;
+ }
+
+ rdata_end = rdata + rdrcnt;
+
+ if (rprcnt < 6) {
+ DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt);
+ res = -1;
+ break;
+ }
+
+ res = rparam ? SVAL(rparam,0) : -1;
+
+ if (res == 0 || res == ERRmoredata ||
+ (res != -1 && cli_errno(cli) == 0)) {
+ char *sname = NULL;
+ int i, count;
+ int converter=SVAL(rparam,2);
+
+ /* Get the number of items returned in this buffer */
+ count = SVAL(rparam, 4);
+
+ /* The next field contains the number of items left,
+ * including those returned in this buffer. So the
+ * first time through this should contain all of the
+ * entries.
+ */
+ if (total_cnt == 0) {
+ total_cnt = SVAL(rparam, 6);
+ }
+
+ /* Keep track of how many we have read */
+ return_cnt += count;
+ p = rdata;
+
+ /* The last name in the previous NetServerEnum reply is
+ * sent back to server in the NetServerEnum3 request
+ * (last_entry). The next reply should repeat this entry
+ * as the first element. We have no proof that this is
+ * always true, but from traces that seems to be the
+ * behavior from Window Servers. So first lets do a lot
+ * of checking, just being paranoid. If the string
+ * matches then we already saw this entry so skip it.
+ *
+ * NOTE: sv1_name field must be null terminated and has
+ * a max size of 16 (NetBIOS Name).
+ */
+ if (last_entry && count && p &&
+ (strncmp(last_entry, p, 16) == 0)) {
+ count -= 1; /* Skip this entry */
+ return_cnt = -1; /* Not part of total, so don't count. */
+ p = rdata + 26; /* Skip the whole record */
+ }
+
+ for (i = 0; i < count; i++, p += 26) {
+ int comment_offset;
+ const char *cmnt;
+ const char *p1;
+ char *s1, *s2;
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint32_t entry_stype;
+
+ if (p + 26 > rdata_end) {
+ TALLOC_FREE(frame);
+ break;
+ }
+
+ sname = p;
+ comment_offset = (IVAL(p,22) & 0xFFFF)-converter;
+ cmnt = comment_offset?(rdata+comment_offset):"";
+
+ if (comment_offset < 0 || comment_offset >= (int)rdrcnt) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ /* Work out the comment length. */
+ for (p1 = cmnt, len = 0; *p1 &&
+ p1 < rdata_end; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+
+ entry_stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+
+ pull_string_talloc(frame,rdata,0,
+ &s1,sname,16,STR_ASCII);
+ pull_string_talloc(frame,rdata,0,
+ &s2,cmnt,len,STR_ASCII);
+
+ if (!s1 || !s2) {
+ TALLOC_FREE(frame);
+ continue;
+ }
+
+ fn(s1, entry_stype, s2, state);
+ TALLOC_FREE(frame);
+ }
+
+ /* We are done with the old last entry, so now we can free it */
+ if (last_entry) {
+ SAFE_FREE(last_entry); /* This will set it to null */
+ }
+
+ /* We always make a copy of the last entry if we have one */
+ if (sname) {
+ last_entry = smb_xstrdup(sname);
+ }
+
+ /* If we have more data, but no last entry then error out */
+ if (!last_entry && (res == ERRmoredata)) {
+ errno = EINVAL;
+ res = 0;
+ }
+
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ } while ((res == ERRmoredata) && (total_cnt > return_cnt));
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ SAFE_FREE(last_entry);
+
+ if (res == -1) {
+ errno = cli_errno(cli);
+ } else {
+ if (!return_cnt) {
+ /* this is a very special case, when the domain master for the
+ work group isn't part of the work group itself, there is something
+ wild going on */
+ errno = ENOENT;
+ }
+ }
+
+ return(return_cnt > 0);
+}
+
+/****************************************************************************
+ Send a SamOEMChangePassword command.
+****************************************************************************/
+
+bool cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password,
+ const char *old_password)
+{
+ char param[1024];
+ unsigned char data[532];
+ char *p = param;
+ unsigned char old_pw_hash[16];
+ unsigned char new_pw_hash[16];
+ unsigned int data_len;
+ unsigned int param_len = 0;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t old_pw_key = {
+ .data = old_pw_hash,
+ .size = sizeof(old_pw_hash),
+ };
+ int rc;
+
+ if (strlen(user) >= sizeof(fstring)-1) {
+ DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user));
+ return False;
+ }
+
+ SSVAL(p,0,214); /* SamOEMChangePassword command. */
+ p += 2;
+ strlcpy(p, "zsT", sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p, "B516B16", sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ strlcpy(p,user, sizeof(param)-PTR_DIFF(p,param));
+ p = skip_string(param,sizeof(param),p);
+ SSVAL(p,0,532);
+ p += 2;
+
+ param_len = PTR_DIFF(p,param);
+
+ /*
+ * Get the Lanman hash of the old password, we
+ * use this as the key to make_oem_passwd_hash().
+ */
+ E_deshash(old_password, old_pw_hash);
+
+ encode_pw_buffer(data, new_password, STR_ASCII);
+
+#ifdef DEBUG_PASSWORD
+ DEBUG(100,("make_oem_passwd_hash\n"));
+ dump_data(100, data, 516);
+#endif
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_pw_key,
+ NULL);
+ if (rc < 0) {
+ DBG_ERR("gnutls_cipher_init failed: %s\n",
+ gnutls_strerror(rc));
+ return false;
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ return false;
+ }
+
+ /*
+ * Now place the old password hash in the data.
+ */
+ E_deshash(new_password, new_pw_hash);
+
+ rc = E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]);
+ if (rc != 0) {
+ DBG_ERR("E_old_pw_hash failed: %s\n", gnutls_strerror(rc));
+ return false;
+ }
+
+ data_len = 532;
+
+ if (!cli_api(cli,
+ param, param_len, 4, /* param, length, max */
+ (char *)data, data_len, 0, /* data, length, max */
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n",
+ user ));
+ return False;
+ }
+
+ if (rdrcnt < 2) {
+ cli->rap_error = ERRbadformat;
+ goto done;
+ }
+
+ if (rparam) {
+ cli->rap_error = SVAL(rparam,0);
+ }
+
+done:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return (cli->rap_error == 0);
+}
+
+static void prep_basic_information_buf(
+ uint8_t buf[40],
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr)
+{
+ char *p = (char *)buf;
+ /*
+ * Add the create, last access, modification, and status change times
+ */
+ put_long_date_full_timespec(
+ TIMESTAMP_SET_NT_OR_BETTER, p, &create_time);
+ p += 8;
+
+ put_long_date_full_timespec(
+ TIMESTAMP_SET_NT_OR_BETTER, p, &access_time);
+ p += 8;
+
+ put_long_date_full_timespec(
+ TIMESTAMP_SET_NT_OR_BETTER, p, &write_time);
+ p += 8;
+
+ put_long_date_full_timespec(
+ TIMESTAMP_SET_NT_OR_BETTER, p, &change_time);
+ p += 8;
+
+ if (attr == (uint32_t)-1 || attr == FILE_ATTRIBUTE_NORMAL) {
+ /* No change. */
+ attr = 0;
+ } else if (attr == 0) {
+ /* Clear all existing attributes. */
+ attr = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ /* Add attributes */
+ SIVAL(p, 0, attr);
+
+ p += 4;
+
+ /* Add padding */
+ SIVAL(p, 0, 0);
+ p += 4;
+
+ SMB_ASSERT(PTR_DIFF(p, buf) == 40);
+}
+
+NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr)
+{
+ uint8_t buf[40];
+
+ prep_basic_information_buf(
+ buf,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ attr);
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ DATA_BLOB in_data = data_blob_const(buf, sizeof(buf));
+ /*
+ * Split out SMB2 here as we need to select
+ * the correct info type and level.
+ */
+ return cli_smb2_setpathinfo(cli,
+ fname,
+ 1, /* SMB2_SETINFO_FILE */
+ SMB_FILE_BASIC_INFORMATION - 1000,
+ &in_data);
+ }
+
+ return cli_setpathinfo(
+ cli, SMB_FILE_BASIC_INFORMATION, fname, buf, sizeof(buf));
+}
+
+struct cli_setfileinfo_ext_state {
+ uint8_t data[40];
+ DATA_BLOB in_data;
+};
+
+static void cli_setfileinfo_ext_done(struct tevent_req *subreq);
+static void cli_setfileinfo_ext_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_setfileinfo_ext_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_setfileinfo_ext_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_setfileinfo_ext_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ prep_basic_information_buf(
+ state->data,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ attr);
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ state->in_data = (DATA_BLOB) {
+ .data = state->data, .length = sizeof(state->data),
+ };
+
+ subreq = cli_smb2_set_info_fnum_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ SMB2_0_INFO_FILE,
+ SMB_FILE_BASIC_INFORMATION - 1000,
+ &state->in_data,
+ 0); /* in_additional_info */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_setfileinfo_ext_done2, req);
+ return req;
+ }
+
+ subreq = cli_setfileinfo_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ SMB_FILE_BASIC_INFORMATION,
+ state->data,
+ sizeof(state->data));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_setfileinfo_ext_done, req);
+ return req;
+}
+
+static void cli_setfileinfo_ext_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_setfileinfo_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_setfileinfo_ext_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_setfileinfo_ext_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_setfileinfo_ext(
+ struct cli_state *cli,
+ uint16_t fnum,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_setfileinfo_ext_send(
+ ev,
+ ev,
+ cli,
+ fnum,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ attr);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_setfileinfo_ext_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level.
+****************************************************************************/
+
+struct cli_qpathinfo2_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ struct timespec create_time;
+ struct timespec access_time;
+ struct timespec write_time;
+ struct timespec change_time;
+ off_t size;
+ uint32_t attr;
+ SMB_INO_T ino;
+ mode_t mode;
+};
+
+static void cli_qpathinfo2_done2(struct tevent_req *subreq);
+static void cli_qpathinfo2_done(struct tevent_req *subreq);
+static void cli_qpathinfo2_got_reparse(struct tevent_req *subreq);
+
+struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_qpathinfo2_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo2_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fname = fname;
+
+ state->mode = S_IFREG;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_qpathinfo_send(state,
+ ev,
+ cli,
+ fname,
+ FSCC_FILE_ALL_INFORMATION,
+ 0x60,
+ UINT16_MAX);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo2_done2, req);
+ return req;
+ }
+ subreq = cli_qpathinfo_send(state, ev, cli, fname,
+ SMB_QUERY_FILE_ALL_INFO,
+ 68, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo2_done, req);
+ return req;
+}
+
+static void cli_qpathinfo2_done2(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_qpathinfo2_state *state =
+ tevent_req_data(req, struct cli_qpathinfo2_state);
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata;
+ NTSTATUS status;
+
+ status = cli_smb2_qpathinfo_recv(subreq, state, &rdata, &num_rdata);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->create_time = interpret_long_date(BVAL(rdata, 0x0));
+ state->access_time = interpret_long_date(BVAL(rdata, 0x8));
+ state->write_time = interpret_long_date(BVAL(rdata, 0x10));
+ state->change_time = interpret_long_date(BVAL(rdata, 0x18));
+ state->attr = PULL_LE_U32(rdata, 0x20);
+ state->size = PULL_LE_U64(rdata, 0x30);
+ state->ino = PULL_LE_U64(rdata, 0x40);
+
+ if (state->attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+ subreq = cli_get_reparse_data_send(state,
+ state->ev,
+ state->cli,
+ state->fname);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_qpathinfo2_got_reparse,
+ req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void cli_qpathinfo2_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qpathinfo2_state *state = tevent_req_data(
+ req, struct cli_qpathinfo2_state);
+ uint8_t *data = NULL;
+ uint32_t num_data;
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &data, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->create_time = interpret_long_date(BVAL(data, 0));
+ state->access_time = interpret_long_date(BVAL(data, 8));
+ state->write_time = interpret_long_date(BVAL(data, 16));
+ state->change_time = interpret_long_date(BVAL(data, 24));
+ state->attr = PULL_LE_U32(data, 32);
+ state->size = PULL_LE_U64(data, 48);
+
+ /*
+ * SMB1 qpathinfo2 uses SMB_QUERY_FILE_ALL_INFO which doesn't
+ * return an inode number (fileid). We can't change this to
+ * one of the FILE_ID info levels as only Win2003 and above
+ * support these [MS-SMB: 2.2.2.3.1] and the SMB1 code needs
+ * to support older servers.
+ */
+ state->ino = 0;
+
+ TALLOC_FREE(data);
+
+ if (state->attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+ subreq = cli_get_reparse_data_send(state,
+ state->ev,
+ state->cli,
+ state->fname);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_qpathinfo2_got_reparse,
+ req);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void cli_qpathinfo2_got_reparse(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_qpathinfo2_state *state =
+ tevent_req_data(req, struct cli_qpathinfo2_state);
+ uint8_t *data = NULL;
+ uint32_t num_data;
+ struct reparse_data_buffer reparse = {
+ .tag = 0,
+ };
+ NTSTATUS status;
+
+ status = cli_get_reparse_data_recv(subreq, state, &data, &num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = reparse_data_buffer_parse(state, &reparse, data, num_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Ignoring unknown reparse data\n");
+ goto done;
+ }
+
+ switch (reparse.tag) {
+ case IO_REPARSE_TAG_SYMLINK:
+ state->mode = S_IFLNK;
+ break;
+ case IO_REPARSE_TAG_NFS:
+ switch (reparse.parsed.nfs.type) {
+ case NFS_SPECFILE_LNK:
+ state->mode = S_IFLNK;
+ break;
+ case NFS_SPECFILE_CHR:
+ state->mode = S_IFCHR;
+ break;
+ case NFS_SPECFILE_BLK:
+ state->mode = S_IFBLK;
+ break;
+ case NFS_SPECFILE_FIFO:
+ state->mode = S_IFIFO;
+ break;
+ case NFS_SPECFILE_SOCK:
+ state->mode = S_IFSOCK;
+ break;
+ }
+ break;
+ }
+done:
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size,
+ uint32_t *pattr,
+ SMB_INO_T *ino,
+ mode_t *mode)
+{
+ struct cli_qpathinfo2_state *state = tevent_req_data(
+ req, struct cli_qpathinfo2_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (create_time) {
+ *create_time = state->create_time;
+ }
+ if (access_time) {
+ *access_time = state->access_time;
+ }
+ if (write_time) {
+ *write_time = state->write_time;
+ }
+ if (change_time) {
+ *change_time = state->change_time;
+ }
+ if (pattr) {
+ *pattr = state->attr;
+ }
+ if (size) {
+ *size = state->size;
+ }
+ if (ino) {
+ *ino = state->ino;
+ }
+ if (mode != NULL) {
+ *mode = state->mode;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_qpathinfo2(struct cli_state *cli,
+ const char *fname,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size,
+ uint32_t *pattr,
+ SMB_INO_T *ino,
+ mode_t *mode)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qpathinfo2_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_qpathinfo2_recv(req,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ size,
+ pattr,
+ ino,
+ mode);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Get the stream info
+****************************************************************************/
+
+struct cli_qpathinfo_streams_state {
+ uint32_t num_data;
+ uint8_t *data;
+};
+
+static void cli_qpathinfo_streams_done(struct tevent_req *subreq);
+static void cli_qpathinfo_streams_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_qpathinfo_streams_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_qpathinfo_streams_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_qpathinfo_send(state,
+ ev,
+ cli,
+ fname,
+ FSCC_FILE_STREAM_INFORMATION,
+ 0,
+ CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_qpathinfo_streams_done2,
+ req);
+ return req;
+ }
+ subreq = cli_qpathinfo_send(state, ev, cli, fname,
+ SMB_FILE_STREAM_INFORMATION,
+ 0, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo_streams_done, req);
+ return req;
+}
+
+static void cli_qpathinfo_streams_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qpathinfo_streams_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_streams_state);
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &state->data,
+ &state->num_data);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_qpathinfo_streams_done2(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_qpathinfo_streams_state *state =
+ tevent_req_data(req, struct cli_qpathinfo_streams_state);
+ NTSTATUS status;
+
+ status = cli_smb2_qpathinfo_recv(subreq,
+ state,
+ &state->data,
+ &state->num_data);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_qpathinfo_streams_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ struct cli_qpathinfo_streams_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_streams_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (!parse_streams_blob(mem_ctx, state->data, state->num_data,
+ pnum_streams, pstreams)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qpathinfo_streams_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_qpathinfo_streams_recv(req, mem_ctx, pnum_streams,
+ pstreams);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata,
+ size_t data_len,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ unsigned int num_streams;
+ struct stream_struct *streams;
+ unsigned int ofs;
+
+ num_streams = 0;
+ streams = NULL;
+ ofs = 0;
+
+ while ((data_len > ofs) && (data_len - ofs >= 24)) {
+ uint32_t nlen, len;
+ size_t size;
+ void *vstr;
+ struct stream_struct *tmp;
+ uint8_t *tmp_buf;
+
+ tmp = talloc_realloc(mem_ctx, streams,
+ struct stream_struct,
+ num_streams+1);
+
+ if (tmp == NULL) {
+ goto fail;
+ }
+ streams = tmp;
+
+ nlen = IVAL(rdata, ofs + 0x04);
+
+ streams[num_streams].size = IVAL_TO_SMB_OFF_T(
+ rdata, ofs + 0x08);
+ streams[num_streams].alloc_size = IVAL_TO_SMB_OFF_T(
+ rdata, ofs + 0x10);
+
+ if (nlen > data_len - (ofs + 24)) {
+ goto fail;
+ }
+
+ /*
+ * We need to null-terminate src, how do I do this with
+ * convert_string_talloc??
+ */
+
+ tmp_buf = talloc_array(streams, uint8_t, nlen+2);
+ if (tmp_buf == NULL) {
+ goto fail;
+ }
+
+ memcpy(tmp_buf, rdata+ofs+24, nlen);
+ tmp_buf[nlen] = 0;
+ tmp_buf[nlen+1] = 0;
+
+ if (!convert_string_talloc(streams, CH_UTF16, CH_UNIX, tmp_buf,
+ nlen+2, &vstr, &size))
+ {
+ TALLOC_FREE(tmp_buf);
+ goto fail;
+ }
+
+ TALLOC_FREE(tmp_buf);
+ streams[num_streams].name = (char *)vstr;
+ num_streams++;
+
+ len = IVAL(rdata, ofs);
+ if (len > data_len - ofs) {
+ goto fail;
+ }
+ if (len == 0) break;
+ ofs += len;
+ }
+
+ *pnum_streams = num_streams;
+ *pstreams = streams;
+ return true;
+
+ fail:
+ TALLOC_FREE(streams);
+ return false;
+}
+
+/****************************************************************************
+ Send a qfileinfo QUERY_FILE_NAME_INFO call.
+****************************************************************************/
+
+struct cli_qfileinfo_basic_state {
+ uint32_t attr;
+ off_t size;
+ struct timespec create_time;
+ struct timespec access_time;
+ struct timespec write_time;
+ struct timespec change_time;
+ SMB_INO_T ino;
+};
+
+static void cli_qfileinfo_basic_done(struct tevent_req *subreq);
+static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq);
+static void cli_qfileinfo_basic_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_qfileinfo_basic_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_qfileinfo_basic_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_qfileinfo_basic_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if ((smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN2) ||
+ cli->win95) {
+ /*
+ * According to
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/3d9d8f3e-dc70-410d-a3fc-6f4a881e8cab
+ * SMB_COM_TRANSACTION2 used in cli_qfileinfo_send()
+ * further down was introduced with the LAN Manager
+ * 1.2 dialect, which we encode as PROTOCOL_LANMAN2.
+ *
+ * The "win95" check was introduced with commit
+ * 27e5850fd3e1c8 in 1998. Hard to check these days,
+ * but leave it in.
+ *
+ * Use a lowerlevel fallback in both cases.
+ */
+
+ subreq = cli_getattrE_send(state, ev, cli, fnum);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_qfileinfo_basic_doneE, req);
+ return req;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_query_info_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 1, /* in_info_type */
+ (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0); /* in_flags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_qfileinfo_basic_done2, req);
+ return req;
+ }
+
+ subreq = cli_qfileinfo_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ SMB_QUERY_FILE_ALL_INFO, /* level */
+ 68, /* min_rdata */
+ CLI_BUFFER_SIZE); /* max_rdata */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qfileinfo_basic_done, req);
+ return req;
+}
+
+static void cli_qfileinfo_basic_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qfileinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_basic_state);
+ uint8_t *rdata;
+ uint32_t num_rdata;
+ NTSTATUS status;
+
+ status = cli_qfileinfo_recv(
+ subreq, state, NULL, &rdata, &num_rdata);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->create_time = interpret_long_date(BVAL(rdata, 0));
+ state->access_time = interpret_long_date(BVAL(rdata, 8));
+ state->write_time = interpret_long_date(BVAL(rdata, 16));
+ state->change_time = interpret_long_date(BVAL(rdata, 24));
+ state->attr = PULL_LE_U32(rdata, 32);
+ state->size = PULL_LE_U64(rdata,48);
+ state->ino = PULL_LE_U32(rdata, 64);
+ TALLOC_FREE(rdata);
+
+ tevent_req_done(req);
+}
+
+static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qfileinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_basic_state);
+ NTSTATUS status;
+
+ status = cli_getattrE_recv(
+ subreq,
+ &state->attr,
+ &state->size,
+ &state->change_time.tv_sec,
+ &state->access_time.tv_sec,
+ &state->write_time.tv_sec);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_qfileinfo_basic_done2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qfileinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_basic_state);
+ DATA_BLOB outbuf = {0};
+ NTSTATUS status;
+
+ status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ /* Parse the reply. */
+ if (outbuf.length < 0x60) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->create_time = interpret_long_date(BVAL(outbuf.data, 0x0));
+ state->access_time = interpret_long_date(BVAL(outbuf.data, 0x8));
+ state->write_time = interpret_long_date(BVAL(outbuf.data, 0x10));
+ state->change_time = interpret_long_date(BVAL(outbuf.data, 0x18));
+ state->attr = IVAL(outbuf.data, 0x20);
+ state->size = BVAL(outbuf.data, 0x30);
+ state->ino = BVAL(outbuf.data, 0x40);
+
+ data_blob_free(&outbuf);
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_qfileinfo_basic_recv(
+ struct tevent_req *req,
+ uint32_t *attr,
+ off_t *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino)
+{
+ struct cli_qfileinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qfileinfo_basic_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (create_time != NULL) {
+ *create_time = state->create_time;
+ }
+ if (access_time != NULL) {
+ *access_time = state->access_time;
+ }
+ if (write_time != NULL) {
+ *write_time = state->write_time;
+ }
+ if (change_time != NULL) {
+ *change_time = state->change_time;
+ }
+ if (attr != NULL) {
+ *attr = state->attr;
+ }
+ if (size != NULL) {
+ *size = state->size;
+ }
+ if (ino) {
+ *ino = state->ino;
+ }
+
+ return NT_STATUS_OK;
+}
+/****************************************************************************
+ Send a qfileinfo call.
+****************************************************************************/
+
+NTSTATUS cli_qfileinfo_basic(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t *attr,
+ off_t *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qfileinfo_basic_send(frame, ev, cli, fnum);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_qfileinfo_basic_recv(
+ req,
+ attr,
+ size,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ ino);
+
+ /* cli_smb2_query_info_fnum_recv doesn't set this */
+ cli->raw_status = status;
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Send a qpathinfo BASIC_INFO call.
+****************************************************************************/
+
+struct cli_qpathinfo_basic_state {
+ uint32_t num_data;
+ uint8_t *data;
+};
+
+static void cli_qpathinfo_basic_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_qpathinfo_basic_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_qpathinfo_basic_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_qpathinfo_basic_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = cli_qpathinfo_send(state, ev, cli, fname,
+ SMB_QUERY_FILE_BASIC_INFO,
+ 36, CLI_BUFFER_SIZE);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_qpathinfo_basic_done, req);
+ return req;
+}
+
+static void cli_qpathinfo_basic_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_qpathinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_basic_state);
+ NTSTATUS status;
+
+ status = cli_qpathinfo_recv(subreq, state, &state->data,
+ &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req,
+ SMB_STRUCT_STAT *sbuf, uint32_t *attributes)
+{
+ struct cli_qpathinfo_basic_state *state = tevent_req_data(
+ req, struct cli_qpathinfo_basic_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ sbuf->st_ex_btime = interpret_long_date(BVAL(state->data, 0));
+ sbuf->st_ex_atime = interpret_long_date(BVAL(state->data, 8));
+ sbuf->st_ex_mtime = interpret_long_date(BVAL(state->data, 16));
+ sbuf->st_ex_ctime = interpret_long_date(BVAL(state->data, 24));
+ *attributes = IVAL(state->data, 32);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name,
+ SMB_STRUCT_STAT *sbuf, uint32_t *attributes)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_qpathinfo_basic(cli,
+ name,
+ sbuf,
+ attributes);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_qpathinfo_basic_send(frame, ev, cli, name);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_qpathinfo_basic_recv(req, sbuf, attributes);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/****************************************************************************
+ Send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call.
+****************************************************************************/
+
+NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name)
+{
+ uint8_t *rdata;
+ uint32_t num_rdata;
+ unsigned int len;
+ char *converted = NULL;
+ size_t converted_size = 0;
+ NTSTATUS status;
+
+ status = cli_qpathinfo(talloc_tos(), cli, fname,
+ SMB_QUERY_FILE_ALT_NAME_INFO,
+ 4, CLI_BUFFER_SIZE, &rdata, &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ len = IVAL(rdata, 0);
+
+ if (len > num_rdata - 4) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ /* The returned data is a pushed string, not raw data. */
+ if (!convert_string_talloc(talloc_tos(),
+ smbXcli_conn_use_unicode(cli->conn) ? CH_UTF16LE : CH_DOS,
+ CH_UNIX,
+ rdata + 4,
+ len,
+ &converted,
+ &converted_size)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ fstrcpy(alt_name, converted);
+
+ TALLOC_FREE(converted);
+ TALLOC_FREE(rdata);
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Send a qpathinfo SMB_QUERY_FILE_STANDARD_INFO call.
+****************************************************************************/
+
+NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname,
+ uint64_t *allocated, uint64_t *size,
+ uint32_t *nlinks,
+ bool *is_del_pending, bool *is_dir)
+{
+ uint8_t *rdata;
+ uint32_t num_rdata;
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ status = cli_qpathinfo(talloc_tos(), cli, fname,
+ SMB_QUERY_FILE_STANDARD_INFO,
+ 24, CLI_BUFFER_SIZE, &rdata, &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (allocated) {
+ *allocated = BVAL(rdata, 0);
+ }
+
+ if (size) {
+ *size = BVAL(rdata, 8);
+ }
+
+ if (nlinks) {
+ *nlinks = IVAL(rdata, 16);
+ }
+
+ if (is_del_pending) {
+ *is_del_pending = CVAL(rdata, 20);
+ }
+
+ if (is_dir) {
+ *is_dir = CVAL(rdata, 20);
+ }
+
+ TALLOC_FREE(rdata);
+
+ return NT_STATUS_OK;
+}
+
+
+/* like cli_qpathinfo2 but do not use SMB_QUERY_FILE_ALL_INFO with smb1 */
+NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size, uint32_t *pattr,
+ SMB_INO_T *ino)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ SMB_STRUCT_STAT st = { 0 };
+ uint32_t attr = 0;
+ uint64_t pos;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ /*
+ * NB. cli_qpathinfo2() checks pattr is valid before
+ * storing a value into it, so we don't need to use
+ * an intermediate attr variable as below but can
+ * pass pattr directly.
+ */
+ return cli_qpathinfo2(cli,
+ fname,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ size,
+ pattr,
+ ino,
+ NULL);
+ }
+
+ if (create_time || access_time || write_time || change_time || pattr) {
+ /*
+ * cli_qpathinfo_basic() always indirects the passed
+ * in pointers so we use intermediate variables to
+ * collect all of them before assigning any requested
+ * below.
+ */
+ status = cli_qpathinfo_basic(cli, fname, &st, &attr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (size) {
+ status = cli_qpathinfo_standard(cli, fname,
+ NULL, &pos, NULL, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *size = pos;
+ }
+
+ if (create_time) {
+ *create_time = st.st_ex_btime;
+ }
+ if (access_time) {
+ *access_time = st.st_ex_atime;
+ }
+ if (write_time) {
+ *write_time = st.st_ex_mtime;
+ }
+ if (change_time) {
+ *change_time = st.st_ex_ctime;
+ }
+ if (pattr) {
+ *pattr = attr;
+ }
+ if (ino) {
+ *ino = 0;
+ }
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/clirap.h b/source3/libsmb/clirap.h
new file mode 100644
index 0000000..4dc0441
--- /dev/null
+++ b/source3/libsmb/clirap.h
@@ -0,0 +1,206 @@
+/*
+ Samba Unix/Linux SMB client library
+ More client RAP (SMB Remote Procedure Calls) functions
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2007 Jeremy Allison. jra@samba.org
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) James Peach 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB_CLIRAP_H
+#define _LIBSMB_CLIRAP_H
+
+struct cli_state;
+
+/* The following definitions come from libsmb/clirap.c */
+
+bool cli_api(struct cli_state *cli,
+ char *param, int prcnt, int mprcnt,
+ char *data, int drcnt, int mdrcnt,
+ char **rparam, unsigned int *rprcnt,
+ char **rdata, unsigned int *rdrcnt);
+int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32_t, const char *, void *), void *state);
+bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32_t stype,
+ void (*fn)(const char *, uint32_t, const char *, void *),
+ void *state);
+bool cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password,
+ const char *old_password);
+NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr);
+struct tevent_req *cli_setfileinfo_ext_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr);
+NTSTATUS cli_setfileinfo_ext_recv(struct tevent_req *req);
+NTSTATUS cli_setfileinfo_ext(
+ struct cli_state *cli,
+ uint16_t fnum,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint32_t attr);
+struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size,
+ uint32_t *pattr,
+ SMB_INO_T *ino,
+ mode_t *mode);
+NTSTATUS cli_qpathinfo2(struct cli_state *cli,
+ const char *fname,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size,
+ uint32_t *pattr,
+ SMB_INO_T *ino,
+ mode_t *mode);
+NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size, uint32_t *pattr,
+ SMB_INO_T *ino);
+struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_qpathinfo_streams_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams);
+NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams);
+bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata,
+ size_t data_len,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams);
+struct tevent_req *cli_qfileinfo_basic_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum);
+NTSTATUS cli_qfileinfo_basic_recv(
+ struct tevent_req *req,
+ uint32_t *attr,
+ off_t *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino);
+NTSTATUS cli_qfileinfo_basic(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t *attr,
+ off_t *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino);
+struct tevent_req *cli_qpathinfo_basic_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req,
+ SMB_STRUCT_STAT *sbuf, uint32_t *attributes);
+NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name,
+ SMB_STRUCT_STAT *sbuf, uint32_t *attributes);
+NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname,
+ uint64_t *allocated, uint64_t *size,
+ uint32_t *nlinks,
+ bool *is_del_pending, bool *is_dir);
+NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name);
+struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata);
+NTSTATUS cli_qpathinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **rdata, uint32_t *num_rdata);
+NTSTATUS cli_qpathinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ const char *fname, uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata,
+ uint8_t **rdata, uint32_t *num_rdata);
+
+struct tevent_req *cli_qfileinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata);
+NTSTATUS cli_qfileinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint16_t *recv_flags2,
+ uint8_t **rdata, uint32_t *num_rdata);
+NTSTATUS cli_qfileinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint16_t fnum, uint16_t level, uint32_t min_rdata,
+ uint32_t max_rdata, uint16_t *recv_flags2,
+ uint8_t **rdata, uint32_t *num_rdata);
+
+struct tevent_req *cli_flush_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum);
+NTSTATUS cli_flush_recv(struct tevent_req *req);
+NTSTATUS cli_flush(TALLOC_CTX *mem_ctx, struct cli_state *cli, uint16_t fnum);
+
+struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names);
+NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char ***pnames, int *pnum_names);
+NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint16_t fnum, bool get_names,
+ char ***pnames, int *pnum_names);
+
+struct tevent_req *cli_fsctl_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t ctl_code,
+ const DATA_BLOB *in,
+ uint32_t max_out);
+NTSTATUS cli_fsctl_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out);
+
+
+#endif /* _LIBSMB_CLIRAP_H */
diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
new file mode 100644
index 0000000..1641905
--- /dev/null
+++ b/source3/libsmb/clireadwrite.c
@@ -0,0 +1,1907 @@
+/*
+ Unix SMB/CIFS implementation.
+ client file read/write routines
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/****************************************************************************
+ Calculate the recommended read buffer size
+****************************************************************************/
+static size_t cli_read_max_bufsize(struct cli_state *cli)
+{
+ uint8_t wct = 12;
+ uint32_t min_space;
+ uint32_t data_offset;
+ uint32_t useable_space = 0;
+
+ data_offset = HDR_VWV;
+ data_offset += wct * sizeof(uint16_t);
+ data_offset += sizeof(uint16_t); /* byte count */
+ data_offset += 1; /* pad */
+
+ min_space = cli_state_available_size(cli, data_offset);
+
+ if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) {
+ useable_space = 0xFFFFFF - data_offset;
+
+ if (smb1cli_conn_signing_is_active(cli->conn)) {
+ return min_space;
+ }
+
+ if (smb1cli_conn_encryption_on(cli->conn)) {
+ return min_space;
+ }
+
+ return useable_space;
+ } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_READX) {
+ /*
+ * Note: CAP_LARGE_READX also works with signing
+ */
+ useable_space = 0x1FFFF - data_offset;
+
+ useable_space = MIN(useable_space, UINT16_MAX);
+
+ return useable_space;
+ }
+
+ return min_space;
+}
+
+/****************************************************************************
+ Calculate the recommended write buffer size
+****************************************************************************/
+static size_t cli_write_max_bufsize(struct cli_state *cli,
+ uint16_t write_mode,
+ uint8_t wct)
+{
+ uint32_t min_space;
+ uint32_t data_offset;
+ uint32_t useable_space = 0;
+
+ data_offset = HDR_VWV;
+ data_offset += wct * sizeof(uint16_t);
+ data_offset += sizeof(uint16_t); /* byte count */
+ data_offset += 1; /* pad */
+
+ min_space = cli_state_available_size(cli, data_offset);
+
+ if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) {
+ useable_space = 0xFFFFFF - data_offset;
+ } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_WRITEX) {
+ useable_space = 0x1FFFF - data_offset;
+ } else {
+ return min_space;
+ }
+
+ if (write_mode != 0) {
+ return min_space;
+ }
+
+ if (smb1cli_conn_signing_is_active(cli->conn)) {
+ return min_space;
+ }
+
+ if (smb1cli_conn_encryption_on(cli->conn)) {
+ return min_space;
+ }
+
+ if (strequal(cli->dev, "LPT1:")) {
+ return min_space;
+ }
+
+ return useable_space;
+}
+
+struct cli_read_andx_state {
+ size_t size;
+ uint16_t vwv[12];
+ NTSTATUS status;
+ size_t received;
+ uint8_t *buf;
+};
+
+static void cli_read_andx_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ off_t offset, size_t size,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_read_andx_state *state;
+ uint8_t wct = 10;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->size = size;
+
+ SCVAL(state->vwv + 0, 0, 0xFF);
+ SCVAL(state->vwv + 0, 1, 0);
+ SSVAL(state->vwv + 1, 0, 0);
+ SSVAL(state->vwv + 2, 0, fnum);
+ SIVAL(state->vwv + 3, 0, offset);
+ SSVAL(state->vwv + 5, 0, size);
+ SSVAL(state->vwv + 6, 0, size);
+ SSVAL(state->vwv + 7, 0, (size >> 16));
+ SSVAL(state->vwv + 8, 0, 0);
+ SSVAL(state->vwv + 9, 0, 0);
+
+ if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) {
+ SIVAL(state->vwv + 10, 0,
+ (((uint64_t)offset)>>32) & 0xffffffff);
+ wct = 12;
+ } else {
+ if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) {
+ DEBUG(10, ("cli_read_andx_send got large offset where "
+ "the server does not support it\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, 0, wct,
+ state->vwv, 0, NULL);
+ if (subreq == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ tevent_req_set_callback(subreq, cli_read_andx_done, req);
+ *psmbreq = subreq;
+ return req;
+}
+
+struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ off_t offset, size_t size)
+{
+ struct tevent_req *req, *subreq;
+ NTSTATUS status;
+
+ req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size,
+ &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void cli_read_andx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_read_andx_state *state = tevent_req_data(
+ req, struct cli_read_andx_state);
+ uint8_t *inbuf;
+ uint8_t wct;
+ uint16_t *vwv;
+ uint32_t num_bytes;
+ uint8_t *bytes;
+
+ state->status = cli_smb_recv(subreq, state, &inbuf, 12, &wct, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_ERR(state->status)) {
+ tevent_req_nterror(req, state->status);
+ return;
+ }
+
+ /* size is the number of bytes the server returned.
+ * Might be zero. */
+ state->received = SVAL(vwv + 5, 0);
+ state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16);
+
+ if (state->received > state->size) {
+ DEBUG(5,("server returned more than we wanted!\n"));
+ tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
+ return;
+ }
+
+ /*
+ * bcc field must be valid for small reads, for large reads the 16-bit
+ * bcc field can't be correct.
+ */
+
+ if ((state->received < 0xffff) && (state->received > num_bytes)) {
+ DEBUG(5, ("server announced more bytes than sent\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ state->buf = discard_const_p(uint8_t, smb_base(inbuf)) + SVAL(vwv+6, 0);
+
+ if (smb_buffer_oob(smb_len_tcp(inbuf), SVAL(vwv+6, 0), state->received)
+ || ((state->received != 0) && (state->buf < bytes))) {
+ DEBUG(5, ("server returned invalid read&x data offset\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+/*
+ * Pull the data out of a finished async read_and_x request. rcvbuf is
+ * talloced from the request, so better make sure that you copy it away before
+ * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not
+ * talloc_move it!
+ */
+
+NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received,
+ uint8_t **rcvbuf)
+{
+ struct cli_read_andx_state *state = tevent_req_data(
+ req, struct cli_read_andx_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *received = state->received;
+ *rcvbuf = state->buf;
+ return NT_STATUS_OK;
+}
+
+struct cli_pull_chunk;
+
+struct cli_pull_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ off_t start_offset;
+ off_t size;
+
+ NTSTATUS (*sink)(char *buf, size_t n, void *priv);
+ void *priv;
+
+ size_t chunk_size;
+ off_t next_offset;
+ off_t remaining;
+
+ /*
+ * How many bytes did we push into "sink"?
+ */
+ off_t pushed;
+
+ /*
+ * Outstanding requests
+ *
+ * The maximum is 256:
+ * - which would be a window of 256 MByte
+ * for SMB2 with multi-credit
+ * or smb1 unix extensions.
+ */
+ uint16_t max_chunks;
+ uint16_t num_chunks;
+ uint16_t num_waiting;
+ struct cli_pull_chunk *chunks;
+};
+
+struct cli_pull_chunk {
+ struct cli_pull_chunk *prev, *next;
+ struct tevent_req *req;/* This is the main request! Not the subreq */
+ struct tevent_req *subreq;
+ off_t ofs;
+ uint8_t *buf;
+ size_t total_size;
+ size_t tmp_size;
+ bool done;
+};
+
+static void cli_pull_setup_chunks(struct tevent_req *req);
+static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk);
+static void cli_pull_chunk_done(struct tevent_req *subreq);
+
+/*
+ * Parallel read support.
+ *
+ * cli_pull sends as many read&x requests as the server would allow via
+ * max_mux at a time. When replies flow back in, the data is written into
+ * the callback function "sink" in the right order.
+ */
+
+struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, off_t start_offset,
+ off_t size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n,
+ void *priv),
+ void *priv)
+{
+ struct tevent_req *req;
+ struct cli_pull_state *state;
+ size_t page_size = 1024;
+ uint64_t tmp64;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_pull_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->ev = ev;
+ state->fnum = fnum;
+ state->start_offset = start_offset;
+ state->size = size;
+ state->sink = sink;
+ state->priv = priv;
+ state->next_offset = start_offset;
+ state->remaining = size;
+
+ if (size == 0) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ state->chunk_size = smb2cli_conn_max_read_size(cli->conn);
+ } else {
+ state->chunk_size = cli_read_max_bufsize(cli);
+ }
+ if (state->chunk_size > page_size) {
+ state->chunk_size &= ~(page_size - 1);
+ }
+
+ if (window_size == 0) {
+ /*
+ * We use 16 MByte as default window size.
+ */
+ window_size = 16 * 1024 * 1024;
+ }
+
+ tmp64 = window_size/state->chunk_size;
+ if ((window_size % state->chunk_size) > 0) {
+ tmp64 += 1;
+ }
+ tmp64 = MAX(tmp64, 1);
+ tmp64 = MIN(tmp64, 256);
+ state->max_chunks = tmp64;
+
+ /*
+ * We defer the callback because of the complex
+ * substate/subfunction logic
+ */
+ tevent_req_defer_callback(req, ev);
+
+ cli_pull_setup_chunks(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void cli_pull_setup_chunks(struct tevent_req *req)
+{
+ struct cli_pull_state *state =
+ tevent_req_data(req,
+ struct cli_pull_state);
+ struct cli_pull_chunk *chunk, *next = NULL;
+ size_t i;
+
+ for (chunk = state->chunks; chunk; chunk = next) {
+ /*
+ * Note that chunk might be removed from this call.
+ */
+ next = chunk->next;
+ cli_pull_chunk_ship(chunk);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ }
+
+ for (i = state->num_chunks; i < state->max_chunks; i++) {
+
+ if (state->num_waiting > 0) {
+ return;
+ }
+
+ if (state->remaining == 0) {
+ break;
+ }
+
+ chunk = talloc_zero(state, struct cli_pull_chunk);
+ if (tevent_req_nomem(chunk, req)) {
+ return;
+ }
+ chunk->req = req;
+ chunk->ofs = state->next_offset;
+ chunk->total_size = MIN(state->remaining, state->chunk_size);
+ state->next_offset += chunk->total_size;
+ state->remaining -= chunk->total_size;
+
+ DLIST_ADD_END(state->chunks, chunk);
+ state->num_chunks++;
+ state->num_waiting++;
+
+ cli_pull_chunk_ship(chunk);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ }
+
+ if (state->remaining > 0) {
+ return;
+ }
+
+ if (state->num_chunks > 0) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk)
+{
+ struct tevent_req *req = chunk->req;
+ struct cli_pull_state *state =
+ tevent_req_data(req,
+ struct cli_pull_state);
+ bool ok;
+ off_t ofs;
+ size_t size;
+
+ if (chunk->done) {
+ NTSTATUS status;
+
+ if (chunk != state->chunks) {
+ /*
+ * this chunk is not the
+ * first one in the list.
+ *
+ * which means we should not
+ * push it into the sink yet.
+ */
+ return;
+ }
+
+ if (chunk->tmp_size == 0) {
+ /*
+ * we got a short read, we're done
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ status = state->sink((char *)chunk->buf,
+ chunk->tmp_size,
+ state->priv);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->pushed += chunk->tmp_size;
+
+ if (chunk->tmp_size < chunk->total_size) {
+ /*
+ * we got a short read, we're done
+ */
+ tevent_req_done(req);
+ return;
+ }
+
+ DLIST_REMOVE(state->chunks, chunk);
+ SMB_ASSERT(state->num_chunks > 0);
+ state->num_chunks--;
+ TALLOC_FREE(chunk);
+
+ return;
+ }
+
+ if (chunk->subreq != NULL) {
+ return;
+ }
+
+ SMB_ASSERT(state->num_waiting > 0);
+
+ ofs = chunk->ofs + chunk->tmp_size;
+ size = chunk->total_size - chunk->tmp_size;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ uint32_t max_size;
+
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (!ok) {
+ return;
+ }
+
+ /*
+ * downgrade depending on the available credits
+ */
+ size = MIN(max_size, size);
+
+ chunk->subreq = cli_smb2_read_send(chunk,
+ state->ev,
+ state->cli,
+ state->fnum,
+ ofs,
+ size);
+ if (tevent_req_nomem(chunk->subreq, req)) {
+ return;
+ }
+ } else {
+ ok = smb1cli_conn_req_possible(state->cli->conn);
+ if (!ok) {
+ return;
+ }
+
+ chunk->subreq = cli_read_andx_send(chunk,
+ state->ev,
+ state->cli,
+ state->fnum,
+ ofs,
+ size);
+ if (tevent_req_nomem(chunk->subreq, req)) {
+ return;
+ }
+ }
+ tevent_req_set_callback(chunk->subreq,
+ cli_pull_chunk_done,
+ chunk);
+
+ state->num_waiting--;
+ return;
+}
+
+static void cli_pull_chunk_done(struct tevent_req *subreq)
+{
+ struct cli_pull_chunk *chunk =
+ tevent_req_callback_data(subreq,
+ struct cli_pull_chunk);
+ struct tevent_req *req = chunk->req;
+ struct cli_pull_state *state =
+ tevent_req_data(req,
+ struct cli_pull_state);
+ NTSTATUS status;
+ size_t expected = chunk->total_size - chunk->tmp_size;
+ ssize_t received = 0;
+ uint8_t *buf = NULL;
+
+ chunk->subreq = NULL;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_read_recv(subreq, &received, &buf);
+ } else {
+ status = cli_read_andx_recv(subreq, &received, &buf);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ received = 0;
+ status = NT_STATUS_OK;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (received > expected) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (received == 0) {
+ /*
+ * We got EOF we're done
+ */
+ chunk->done = true;
+ cli_pull_setup_chunks(req);
+ return;
+ }
+
+ if (received == chunk->total_size) {
+ /*
+ * We got it in the first run.
+ *
+ * We don't call TALLOC_FREE(subreq)
+ * here and keep the returned buffer.
+ */
+ chunk->buf = buf;
+ } else if (chunk->buf == NULL) {
+ chunk->buf = talloc_array(chunk, uint8_t, chunk->total_size);
+ if (tevent_req_nomem(chunk->buf, req)) {
+ return;
+ }
+ }
+
+ if (received != chunk->total_size) {
+ uint8_t *p = chunk->buf + chunk->tmp_size;
+ memcpy(p, buf, received);
+ TALLOC_FREE(subreq);
+ }
+
+ chunk->tmp_size += received;
+
+ if (chunk->tmp_size == chunk->total_size) {
+ chunk->done = true;
+ } else {
+ state->num_waiting++;
+ }
+
+ cli_pull_setup_chunks(req);
+}
+
+NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received)
+{
+ struct cli_pull_state *state = tevent_req_data(
+ req, struct cli_pull_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *received = state->pushed;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
+ off_t start_offset, off_t size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n, void *priv),
+ void *priv, off_t *received)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_pull_send(frame, ev, cli, fnum, start_offset, size,
+ window_size, sink, priv);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_pull_recv(req, received);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_read_state {
+ struct cli_state *cli;
+ char *buf;
+ size_t buflen;
+ size_t received;
+};
+
+static void cli_read_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_read_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ char *buf,
+ off_t offset,
+ size_t size)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->buf = buf;
+ state->buflen = size;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ uint32_t max_size;
+ bool ok;
+
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (!ok) {
+ tevent_req_nterror(
+ req,
+ NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * downgrade depending on the available credits
+ */
+ size = MIN(max_size, size);
+
+ subreq = cli_smb2_read_send(
+ state, ev, cli, fnum, offset, size);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ } else {
+ bool ok;
+ ok = smb1cli_conn_req_possible(state->cli->conn);
+ if (!ok) {
+ tevent_req_nterror(
+ req,
+ NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_read_andx_send(
+ state, ev, cli, fnum, offset, size);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ tevent_req_set_callback(subreq, cli_read_done, req);
+
+ return req;
+}
+
+static void cli_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_read_state *state = tevent_req_data(
+ req, struct cli_read_state);
+ NTSTATUS status;
+ ssize_t received;
+ uint8_t *buf = NULL;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_read_recv(subreq, &received, &buf);
+ } else {
+ status = cli_read_andx_recv(subreq, &received, &buf);
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
+ received = 0;
+ status = NT_STATUS_OK;
+ }
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if ((buf == NULL) || (received < 0) || (received > state->buflen)) {
+ state->received = 0;
+ tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
+ return;
+ }
+
+ memcpy(state->buf, buf, received);
+ state->received = received;
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received)
+{
+ struct cli_read_state *state = tevent_req_data(
+ req, struct cli_read_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (received != NULL) {
+ *received = state->received;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Helper function for cli_pull(). This takes a chunk of data (buf) read from
+ * a remote file and copies it into the return buffer (priv).
+ */
+NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
+{
+ char **pbuf = (char **)priv;
+ memcpy(*pbuf, buf, n);
+ *pbuf += n;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum,
+ char *buf, off_t offset, size_t size,
+ size_t *nread)
+{
+ NTSTATUS status;
+ off_t ret = 0;
+
+ status = cli_pull(cli, fnum, offset, size, size,
+ cli_read_sink, &buf, &ret);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (nread) {
+ *nread = ret;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ write to a file using a SMBwrite and not bypassing 0 byte writes
+****************************************************************************/
+
+NTSTATUS cli_smbwrite(struct cli_state *cli, uint16_t fnum, char *buf,
+ off_t offset, size_t size1, size_t *ptotal)
+{
+ uint8_t *bytes;
+ ssize_t total = 0;
+
+ /*
+ * 3 bytes prefix
+ */
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 3);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 1;
+
+ do {
+ uint32_t usable_space = cli_state_available_size(cli, 48);
+ size_t size = MIN(size1, usable_space);
+ struct tevent_req *req;
+ uint16_t vwv[5];
+ uint16_t *ret_vwv;
+ NTSTATUS status;
+
+ SSVAL(vwv+0, 0, fnum);
+ SSVAL(vwv+1, 0, size);
+ SIVAL(vwv+2, 0, offset);
+ SSVAL(vwv+4, 0, 0);
+
+ bytes = talloc_realloc(talloc_tos(), bytes, uint8_t,
+ size+3);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ SSVAL(bytes, 1, size);
+ memcpy(bytes + 3, buf + total, size);
+
+ status = cli_smb(talloc_tos(), cli, SMBwrite, 0, 5, vwv,
+ size+3, bytes, &req, 1, NULL, &ret_vwv,
+ NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(bytes);
+ return status;
+ }
+
+ size = SVAL(ret_vwv+0, 0);
+ TALLOC_FREE(req);
+ if (size == 0) {
+ break;
+ }
+ size1 -= size;
+ total += size;
+ offset += size;
+
+ } while (size1);
+
+ TALLOC_FREE(bytes);
+
+ if (ptotal != NULL) {
+ *ptotal = total;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Send a write&x request
+ */
+
+struct cli_write_andx_state {
+ size_t size;
+ uint16_t vwv[14];
+ size_t written;
+ uint8_t pad;
+ struct iovec iov[2];
+};
+
+static void cli_write_andx_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size,
+ struct tevent_req **reqs_before,
+ int num_reqs_before,
+ struct tevent_req **psmbreq)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_write_andx_state *state;
+ bool bigoffset = ((smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) != 0);
+ uint8_t wct = bigoffset ? 14 : 12;
+ size_t max_write = cli_write_max_bufsize(cli, mode, wct);
+ uint16_t *vwv;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->size = MIN(size, max_write);
+
+ vwv = state->vwv;
+
+ SCVAL(vwv+0, 0, 0xFF);
+ SCVAL(vwv+0, 1, 0);
+ SSVAL(vwv+1, 0, 0);
+ SSVAL(vwv+2, 0, fnum);
+ SIVAL(vwv+3, 0, offset);
+ SIVAL(vwv+5, 0, 0);
+ SSVAL(vwv+7, 0, mode);
+ SSVAL(vwv+8, 0, 0);
+ SSVAL(vwv+9, 0, (state->size>>16));
+ SSVAL(vwv+10, 0, state->size);
+
+ SSVAL(vwv+11, 0,
+ smb1cli_req_wct_ofs(reqs_before, num_reqs_before)
+ + 1 /* the wct field */
+ + wct * 2 /* vwv */
+ + 2 /* num_bytes field */
+ + 1 /* pad */);
+
+ if (bigoffset) {
+ SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff);
+ }
+
+ state->pad = 0;
+ state->iov[0].iov_base = (void *)&state->pad;
+ state->iov[0].iov_len = 1;
+ state->iov[1].iov_base = discard_const_p(void, buf);
+ state->iov[1].iov_len = state->size;
+
+ subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, 0, wct, vwv,
+ 2, state->iov);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_write_andx_done, req);
+ *psmbreq = subreq;
+ return req;
+}
+
+struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size)
+{
+ struct tevent_req *req, *subreq;
+ NTSTATUS status;
+
+ req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset,
+ size, NULL, 0, &subreq);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void cli_write_andx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_write_andx_state *state = tevent_req_data(
+ req, struct cli_write_andx_state);
+ uint8_t wct;
+ uint16_t *vwv;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq, state, NULL, 6, &wct, &vwv,
+ NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_ERR(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ state->written = SVAL(vwv+2, 0);
+ if (state->size > UINT16_MAX) {
+ /*
+ * It is important that we only set the
+ * high bits only if we asked for a large write.
+ *
+ * OS/2 print shares get this wrong and may send
+ * invalid values.
+ *
+ * See bug #5326.
+ */
+ state->written |= SVAL(vwv+4, 0)<<16;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten)
+{
+ struct cli_write_andx_state *state = tevent_req_data(
+ req, struct cli_write_andx_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pwritten != 0) {
+ *pwritten = state->written;
+ }
+ return NT_STATUS_OK;
+}
+
+struct cli_write_state {
+ struct cli_state *cli;
+ size_t written;
+};
+
+static void cli_write_done(struct tevent_req *subreq);
+
+/*
+ * Used to write to a file remotely.
+ * This is similar in functionality to cli_push_send(), except this is a more
+ * finer-grain API. For example, if the data we want to write exceeds the max
+ * write size of the underlying connection, then it's the caller's
+ * responsibility to handle this.
+ * For writing a small amount of data to file, this is a simpler API to use.
+ */
+struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size)
+{
+ struct tevent_req *req = NULL;
+ struct cli_write_state *state = NULL;
+ struct tevent_req *subreq = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ uint32_t max_size;
+ bool ok;
+
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (!ok) {
+ tevent_req_nterror(
+ req,
+ NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * downgrade depending on the available credits
+ */
+ size = MIN(max_size, size);
+
+ subreq = cli_smb2_write_send(state,
+ ev,
+ cli,
+ fnum,
+ mode,
+ buf,
+ offset,
+ size);
+ } else {
+ bool ok;
+
+ ok = smb1cli_conn_req_possible(state->cli->conn);
+ if (!ok) {
+ tevent_req_nterror(
+ req,
+ NT_STATUS_INSUFFICIENT_RESOURCES);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_write_andx_send(state,
+ ev,
+ cli,
+ fnum,
+ mode,
+ buf,
+ offset,
+ size);
+ }
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_write_done, req);
+
+ return req;
+}
+
+static void cli_write_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct cli_write_state *state =
+ tevent_req_data(req,
+ struct cli_write_state);
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_write_recv(subreq, &state->written);
+ } else {
+ status = cli_write_andx_recv(subreq, &state->written);
+ }
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten)
+{
+ struct cli_write_state *state =
+ tevent_req_data(req,
+ struct cli_write_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ if (pwritten != NULL) {
+ *pwritten = state->written;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct cli_smb1_writeall_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ uint16_t mode;
+ const uint8_t *buf;
+ off_t offset;
+ size_t size;
+ size_t written;
+};
+
+static void cli_smb1_writeall_written(struct tevent_req *req);
+
+static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset, size_t size)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb1_writeall_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb1_writeall_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fnum = fnum;
+ state->mode = mode;
+ state->buf = buf;
+ state->offset = offset;
+ state->size = size;
+ state->written = 0;
+
+ subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum,
+ state->mode, state->buf, state->offset,
+ state->size);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
+ return req;
+}
+
+static void cli_smb1_writeall_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb1_writeall_state *state = tevent_req_data(
+ req, struct cli_smb1_writeall_state);
+ NTSTATUS status;
+ size_t written = 0, to_write;
+
+ status = cli_write_andx_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->written += written;
+
+ if (state->written > state->size) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ to_write = state->size - state->written;
+
+ if (to_write == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum,
+ state->mode,
+ state->buf + state->written,
+ state->offset + state->written, to_write);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
+}
+
+static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
+ size_t *pwritten)
+{
+ struct cli_smb1_writeall_state *state = tevent_req_data(
+ req, struct cli_smb1_writeall_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pwritten != NULL) {
+ *pwritten = state->written;
+ }
+ return NT_STATUS_OK;
+}
+
+struct cli_writeall_state {
+ struct cli_state *cli;
+ size_t written;
+};
+
+static void cli_writeall_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_writeall_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_writeall_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_writeall_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ mode,
+ buf,
+ offset,
+ size);
+ } else {
+ subreq = cli_smb1_writeall_send(
+ state,
+ ev,
+ cli,
+ fnum,
+ mode,
+ buf,
+ offset,
+ size);
+ }
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_writeall_done, req);
+
+ return req;
+}
+
+static void cli_writeall_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_writeall_state *state = tevent_req_data(
+ req, struct cli_writeall_state);
+ NTSTATUS status;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_writeall_recv(subreq, &state->written);
+ } else {
+ status = cli_smb1_writeall_recv(subreq, &state->written);
+ }
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten)
+{
+ struct cli_writeall_state *state = tevent_req_data(
+ req, struct cli_writeall_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pwritten != NULL) {
+ *pwritten = state->written;
+ }
+ return NT_STATUS_OK;
+}
+
+
+NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
+ const uint8_t *buf, off_t offset, size_t size,
+ size_t *pwritten)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_writeall_recv(req, pwritten);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_push_chunk;
+
+struct cli_push_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ uint16_t mode;
+ off_t start_offset;
+
+ size_t (*source)(uint8_t *buf, size_t n, void *priv);
+ void *priv;
+
+ bool eof;
+
+ size_t chunk_size;
+ off_t next_offset;
+
+ /*
+ * Outstanding requests
+ *
+ * The maximum is 256:
+ * - which would be a window of 256 MByte
+ * for SMB2 with multi-credit
+ * or smb1 unix extensions.
+ */
+ uint16_t max_chunks;
+ uint16_t num_chunks;
+ uint16_t num_waiting;
+ struct cli_push_chunk *chunks;
+};
+
+struct cli_push_chunk {
+ struct cli_push_chunk *prev, *next;
+ struct tevent_req *req;/* This is the main request! Not the subreq */
+ struct tevent_req *subreq;
+ off_t ofs;
+ uint8_t *buf;
+ size_t total_size;
+ size_t tmp_size;
+ bool done;
+};
+
+static void cli_push_setup_chunks(struct tevent_req *req);
+static void cli_push_chunk_ship(struct cli_push_chunk *chunk);
+static void cli_push_chunk_done(struct tevent_req *subreq);
+
+/*
+ * Used to write to a file remotely.
+ * This is similar in functionality to cli_write_send(), except this API
+ * handles writing a large file by breaking the data into chunks (so we don't
+ * exceed the max write size of the underlying connection). To do this, the
+ * (*source) callback handles copying the underlying file data into a message
+ * buffer, one chunk at a time.
+ * This API is recommended when writing a potentially large amount of data,
+ * e.g. when copying a file (or doing a 'put').
+ */
+struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, uint16_t mode,
+ off_t start_offset, size_t window_size,
+ size_t (*source)(uint8_t *buf, size_t n,
+ void *priv),
+ void *priv)
+{
+ struct tevent_req *req;
+ struct cli_push_state *state;
+ size_t page_size = 1024;
+ uint64_t tmp64;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_push_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->ev = ev;
+ state->fnum = fnum;
+ state->start_offset = start_offset;
+ state->mode = mode;
+ state->source = source;
+ state->priv = priv;
+ state->next_offset = start_offset;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ state->chunk_size = smb2cli_conn_max_write_size(cli->conn);
+ } else {
+ state->chunk_size = cli_write_max_bufsize(cli, mode, 14);
+ }
+ if (state->chunk_size > page_size) {
+ state->chunk_size &= ~(page_size - 1);
+ }
+
+ if (window_size == 0) {
+ /*
+ * We use 16 MByte as default window size.
+ */
+ window_size = 16 * 1024 * 1024;
+ }
+
+ tmp64 = window_size/state->chunk_size;
+ if ((window_size % state->chunk_size) > 0) {
+ tmp64 += 1;
+ }
+ tmp64 = MAX(tmp64, 1);
+ tmp64 = MIN(tmp64, 256);
+ state->max_chunks = tmp64;
+
+ /*
+ * We defer the callback because of the complex
+ * substate/subfunction logic
+ */
+ tevent_req_defer_callback(req, ev);
+
+ cli_push_setup_chunks(req);
+ if (!tevent_req_is_in_progress(req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static void cli_push_setup_chunks(struct tevent_req *req)
+{
+ struct cli_push_state *state =
+ tevent_req_data(req,
+ struct cli_push_state);
+ struct cli_push_chunk *chunk, *next = NULL;
+ size_t i;
+
+ for (chunk = state->chunks; chunk; chunk = next) {
+ /*
+ * Note that chunk might be removed from this call.
+ */
+ next = chunk->next;
+ cli_push_chunk_ship(chunk);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ }
+
+ for (i = state->num_chunks; i < state->max_chunks; i++) {
+
+ if (state->num_waiting > 0) {
+ return;
+ }
+
+ if (state->eof) {
+ break;
+ }
+
+ chunk = talloc_zero(state, struct cli_push_chunk);
+ if (tevent_req_nomem(chunk, req)) {
+ return;
+ }
+ chunk->req = req;
+ chunk->ofs = state->next_offset;
+ chunk->buf = talloc_array(chunk,
+ uint8_t,
+ state->chunk_size);
+ if (tevent_req_nomem(chunk->buf, req)) {
+ return;
+ }
+ chunk->total_size = state->source(chunk->buf,
+ state->chunk_size,
+ state->priv);
+ if (chunk->total_size == 0) {
+ /* nothing to send */
+ talloc_free(chunk);
+ state->eof = true;
+ break;
+ }
+ state->next_offset += chunk->total_size;
+
+ DLIST_ADD_END(state->chunks, chunk);
+ state->num_chunks++;
+ state->num_waiting++;
+
+ cli_push_chunk_ship(chunk);
+ if (!tevent_req_is_in_progress(req)) {
+ return;
+ }
+ }
+
+ if (!state->eof) {
+ return;
+ }
+
+ if (state->num_chunks > 0) {
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static void cli_push_chunk_ship(struct cli_push_chunk *chunk)
+{
+ struct tevent_req *req = chunk->req;
+ struct cli_push_state *state =
+ tevent_req_data(req,
+ struct cli_push_state);
+ bool ok;
+ const uint8_t *buf;
+ off_t ofs;
+ size_t size;
+
+ if (chunk->done) {
+ DLIST_REMOVE(state->chunks, chunk);
+ SMB_ASSERT(state->num_chunks > 0);
+ state->num_chunks--;
+ TALLOC_FREE(chunk);
+
+ return;
+ }
+
+ if (chunk->subreq != NULL) {
+ return;
+ }
+
+ SMB_ASSERT(state->num_waiting > 0);
+
+ buf = chunk->buf + chunk->tmp_size;
+ ofs = chunk->ofs + chunk->tmp_size;
+ size = chunk->total_size - chunk->tmp_size;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ uint32_t max_size;
+
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (!ok) {
+ return;
+ }
+
+ /*
+ * downgrade depending on the available credits
+ */
+ size = MIN(max_size, size);
+
+ chunk->subreq = cli_smb2_write_send(chunk,
+ state->ev,
+ state->cli,
+ state->fnum,
+ state->mode,
+ buf,
+ ofs,
+ size);
+ if (tevent_req_nomem(chunk->subreq, req)) {
+ return;
+ }
+ } else {
+ ok = smb1cli_conn_req_possible(state->cli->conn);
+ if (!ok) {
+ return;
+ }
+
+ chunk->subreq = cli_write_andx_send(chunk,
+ state->ev,
+ state->cli,
+ state->fnum,
+ state->mode,
+ buf,
+ ofs,
+ size);
+ if (tevent_req_nomem(chunk->subreq, req)) {
+ return;
+ }
+ }
+ tevent_req_set_callback(chunk->subreq,
+ cli_push_chunk_done,
+ chunk);
+
+ state->num_waiting--;
+ return;
+}
+
+static void cli_push_chunk_done(struct tevent_req *subreq)
+{
+ struct cli_push_chunk *chunk =
+ tevent_req_callback_data(subreq,
+ struct cli_push_chunk);
+ struct tevent_req *req = chunk->req;
+ struct cli_push_state *state =
+ tevent_req_data(req,
+ struct cli_push_state);
+ NTSTATUS status;
+ size_t expected = chunk->total_size - chunk->tmp_size;
+ size_t written;
+
+ chunk->subreq = NULL;
+
+ if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_write_recv(subreq, &written);
+ } else {
+ status = cli_write_andx_recv(subreq, &written);
+ }
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (written > expected) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (written == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ chunk->tmp_size += written;
+
+ if (chunk->tmp_size == chunk->total_size) {
+ chunk->done = true;
+ } else {
+ state->num_waiting++;
+ }
+
+ cli_push_setup_chunks(req);
+}
+
+NTSTATUS cli_push_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
+ off_t start_offset, size_t window_size,
+ size_t (*source)(uint8_t *buf, size_t n, void *priv),
+ void *priv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = cli_push_send(frame, ev, cli, fnum, mode, start_offset,
+ window_size, source, priv);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = cli_push_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+#define SPLICE_BLOCK_SIZE 1024 * 1024
+
+static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame,
+ struct cli_state *srccli,
+ struct cli_state *dstcli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t initial_size,
+ off_t src_offset, off_t dst_offset,
+ off_t *written,
+ int (*splice_cb)(off_t n, void *priv),
+ void *priv)
+{
+ NTSTATUS status;
+ uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE);
+ size_t nread;
+ off_t remaining = initial_size;
+ *written = 0;
+
+ while (remaining) {
+ size_t to_read = MIN(remaining, SPLICE_BLOCK_SIZE);
+
+ status = cli_read(srccli, src_fnum,
+ (char *)buf, src_offset, to_read,
+ &nread);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = cli_writeall(dstcli, dst_fnum, 0,
+ buf, dst_offset, nread, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((src_offset > INT64_MAX - nread) ||
+ (dst_offset > INT64_MAX - nread)) {
+ return NT_STATUS_FILE_TOO_LARGE;
+ }
+ src_offset += nread;
+ dst_offset += nread;
+ *written += nread;
+ if (remaining < nread) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ remaining -= nread;
+ if (!splice_cb(initial_size - remaining, priv)) {
+ return NT_STATUS_CANCELLED;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size,
+ off_t src_offset, off_t dst_offset,
+ off_t *written,
+ int (*splice_cb)(off_t n, void *priv), void *priv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+ bool retry_fallback = false;
+
+ if (smbXcli_conn_has_async_calls(srccli->conn) ||
+ smbXcli_conn_has_async_calls(dstcli->conn))
+ {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ do {
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto out;
+ }
+ if (srccli == dstcli &&
+ smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 &&
+ !retry_fallback)
+ {
+ req = cli_smb2_splice_send(frame, ev,
+ srccli, src_fnum, dst_fnum,
+ size, src_offset, dst_offset,
+ splice_cb, priv);
+ } else {
+ status = cli_splice_fallback(frame,
+ srccli, dstcli,
+ src_fnum, dst_fnum,
+ size,
+ src_offset, dst_offset,
+ written,
+ splice_cb, priv);
+ goto out;
+ }
+ if (req == NULL) {
+ goto out;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ status = map_nt_error_from_unix(errno);
+ goto out;
+ }
+ status = cli_smb2_splice_recv(req, written);
+
+ /*
+ * Older versions of Samba don't support
+ * FSCTL_SRV_COPYCHUNK_WRITE so use the fallback.
+ */
+ retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST);
+ } while (retry_fallback);
+
+ out:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c
new file mode 100644
index 0000000..d6a2661
--- /dev/null
+++ b/source3/libsmb/clisecdesc.c
@@ -0,0 +1,372 @@
+/*
+ Unix SMB/CIFS implementation.
+ client security descriptor functions
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/security/secdesc.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/util/tevent_ntstatus.h"
+
+struct cli_query_security_descriptor_state {
+ uint8_t param[8];
+ DATA_BLOB outbuf;
+};
+
+static void cli_query_security_descriptor_done1(struct tevent_req *subreq);
+static void cli_query_security_descriptor_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_query_security_descriptor_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_query_security_descriptor_state *state = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_query_security_descriptor_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_query_info_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 3, /* in_info_type */
+ 0, /* in_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ sec_info, /* in_additional_info */
+ 0); /* in_flags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_query_security_descriptor_done2, req);
+ return req;
+ }
+
+ PUSH_LE_U32(state->param, 0, fnum);
+ PUSH_LE_U32(state->param, 4, sec_info);
+
+ subreq = cli_trans_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ 0, /* additional_flags2 */
+ SMBnttrans, /* cmd */
+ NULL, /* pipe_name */
+ -1, /* fid */
+ NT_TRANSACT_QUERY_SECURITY_DESC, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ state->param, /* param */
+ 8, /* num_param */
+ 4, /* max_param */
+ NULL, /* data */
+ 0, /* num_data */
+ 0x10000); /* max_data */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_query_security_descriptor_done1, req);
+ return req;
+}
+
+static void cli_query_security_descriptor_done1(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_query_security_descriptor_state *state = tevent_req_data(
+ req, struct cli_query_security_descriptor_state);
+ NTSTATUS status;
+ uint32_t len;
+
+ status = cli_trans_recv(
+ subreq, /* req */
+ state, /* mem_ctx */
+ NULL, /* recv_flags2 */
+ NULL, /* setup */
+ 0, /* min_setup */
+ NULL, /* num_setup */
+ NULL, /* param */
+ 0, /* min_param */
+ NULL, /* num_param */
+ &state->outbuf.data, /* data */
+ 0, /* min_data */
+ &len); /* num_data */
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->outbuf.length = len; /* uint32_t -> size_t */
+ tevent_req_done(req);
+}
+
+static void cli_query_security_descriptor_done2(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_query_security_descriptor_state *state = tevent_req_data(
+ req, struct cli_query_security_descriptor_state);
+ NTSTATUS status;
+
+ status = cli_smb2_query_info_fnum_recv(subreq, state, &state->outbuf);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_query_security_descriptor_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ struct cli_query_security_descriptor_state *state = tevent_req_data(
+ req, struct cli_query_security_descriptor_state);
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ goto done;
+ }
+ if (sd != NULL) {
+ status = unmarshall_sec_desc(
+ mem_ctx, state->outbuf.data, state->outbuf.length, sd);
+ }
+done:
+ tevent_req_received(req);
+ return status;
+}
+
+NTSTATUS cli_query_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_query_security_descriptor_send(
+ frame, ev, cli, fnum, sec_info);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_query_security_descriptor_recv(req, mem_ctx, sd);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_query_secdesc(struct cli_state *cli, uint16_t fnum,
+ TALLOC_CTX *mem_ctx, struct security_descriptor **sd)
+{
+ uint32_t sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
+
+ return cli_query_security_descriptor(cli, fnum, sec_info, mem_ctx, sd);
+}
+
+NTSTATUS cli_query_mxac(struct cli_state *cli,
+ const char *filename,
+ uint32_t *mxac)
+{
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return cli_smb2_query_mxac(cli, filename, mxac);
+}
+
+struct cli_set_security_descriptor_state {
+ uint8_t param[8];
+ DATA_BLOB buf;
+};
+
+static void cli_set_security_descriptor_done1(struct tevent_req *subreq);
+static void cli_set_security_descriptor_done2(struct tevent_req *subreq);
+
+struct tevent_req *cli_set_security_descriptor_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_set_security_descriptor_state *state = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_set_security_descriptor_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ status = marshall_sec_desc(
+ state, sd, &state->buf.data, &state->buf.length);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ subreq = cli_smb2_set_info_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 3, /* in_info_type */
+ 0, /* in_file_info_class */
+ &state->buf, /* in_input_buffer */
+ sec_info); /* in_additional_info */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_set_security_descriptor_done2, req);
+ return req;
+ }
+
+ SIVAL(state->param, 0, fnum);
+ SIVAL(state->param, 4, sec_info);
+
+ subreq = cli_trans_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ 0, /* additional_flags2 */
+ SMBnttrans, /* cmd */
+ NULL, /* pipe_name */
+ -1, /* fid */
+ NT_TRANSACT_SET_SECURITY_DESC, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ state->param, /* param */
+ 8, /* num_param */
+ 0, /* max_param */
+ state->buf.data, /* data */
+ state->buf.length, /* num_data */
+ 0); /* max_data */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ subreq, cli_set_security_descriptor_done1, req);
+ return req;
+}
+
+static void cli_set_security_descriptor_done1(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(
+ subreq, NULL, NULL, NULL, 0, NULL, NULL, 0, NULL,
+ NULL, 0, NULL);
+ return tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_set_security_descriptor_done2(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_set_security_descriptor_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/****************************************************************************
+ set the security descriptor for a open file
+ ****************************************************************************/
+NTSTATUS cli_set_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_set_security_descriptor_send(
+ frame, ev, cli, fnum, sec_info, sd);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_set_security_descriptor_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+NTSTATUS cli_set_secdesc(struct cli_state *cli, uint16_t fnum,
+ const struct security_descriptor *sd)
+{
+ uint32_t sec_info = 0;
+
+ if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
+ sec_info |= SECINFO_DACL;
+ }
+ if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
+ sec_info |= SECINFO_SACL;
+ }
+ if (sd->owner_sid) {
+ sec_info |= SECINFO_OWNER;
+ }
+ if (sd->group_sid) {
+ sec_info |= SECINFO_GROUP;
+ }
+
+ return cli_set_security_descriptor(cli, fnum, sec_info, sd);
+}
diff --git a/source3/libsmb/clisymlink.c b/source3/libsmb/clisymlink.c
new file mode 100644
index 0000000..d7ecf54
--- /dev/null
+++ b/source3/libsmb/clisymlink.c
@@ -0,0 +1,650 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Client implementation of setting symlinks using reparse points
+ * Copyright (C) Volker Lendecke 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "libsmb/clirap.h"
+#include "trans2.h"
+#include "libcli/security/secdesc.h"
+#include "libcli/security/security.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/smb/reparse.h"
+
+struct cli_create_reparse_point_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ DATA_BLOB reparse_blob;
+ uint16_t fnum;
+ NTSTATUS set_reparse_status;
+};
+
+static void cli_create_reparse_point_opened(struct tevent_req *subreq);
+static void cli_create_reparse_point_done(struct tevent_req *subreq);
+static void cli_create_reparse_point_doc_done(struct tevent_req *subreq);
+static void cli_create_reparse_point_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_create_reparse_point_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ DATA_BLOB reparse_blob)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_create_reparse_point_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct cli_create_reparse_point_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->reparse_blob = reparse_blob;
+
+ /*
+ * The create arguments were taken from a Windows->Windows
+ * symlink create call.
+ */
+ subreq = cli_ntcreate_send(
+ state,
+ ev,
+ cli,
+ fname,
+ 0,
+ SYNCHRONIZE_ACCESS | DELETE_ACCESS | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_NONE,
+ FILE_CREATE,
+ FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT |
+ FILE_NON_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_create_reparse_point_opened, req);
+ return req;
+}
+
+static void cli_create_reparse_point_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_create_reparse_point_state *state =
+ tevent_req_data(req, struct cli_create_reparse_point_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_fsctl_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ FSCTL_SET_REPARSE_POINT,
+ &state->reparse_blob,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_create_reparse_point_done, req);
+}
+
+static void cli_create_reparse_point_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_create_reparse_point_state *state =
+ tevent_req_data(req, struct cli_create_reparse_point_state);
+
+ state->set_reparse_status = cli_fsctl_recv(subreq, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(state->set_reparse_status)) {
+ subreq = cli_close_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_create_reparse_point_closed,
+ req);
+ return;
+ }
+ subreq = cli_nt_delete_on_close_send(
+ state, state->ev, state->cli, state->fnum, true);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_create_reparse_point_doc_done,
+ req);
+}
+
+static void cli_create_reparse_point_doc_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_create_reparse_point_state *state =
+ tevent_req_data(req, struct cli_create_reparse_point_state);
+
+ /*
+ * Ignore status, we can't do much anyway in case of failure
+ */
+
+ (void)cli_nt_delete_on_close_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_create_reparse_point_closed, req);
+}
+
+static void cli_create_reparse_point_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_create_reparse_point_state *state =
+ tevent_req_data(req, struct cli_create_reparse_point_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (tevent_req_nterror(req, state->set_reparse_status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_create_reparse_point_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_symlink_state {
+ uint8_t dummy;
+};
+
+static void cli_symlink_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *link_target,
+ const char *newpath,
+ uint32_t flags)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_symlink_state *state = NULL;
+ struct reparse_data_buffer reparse_buf = {
+ .tag = IO_REPARSE_TAG_SYMLINK,
+ .parsed.lnk.substitute_name =
+ discard_const_p(char, link_target),
+ .parsed.lnk.print_name = discard_const_p(char, link_target),
+ .parsed.lnk.flags = flags,
+ };
+ uint8_t *buf;
+ ssize_t buflen;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ buflen = reparse_data_buffer_marshall(&reparse_buf, NULL, 0);
+ if (buflen == -1) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
+ }
+
+ buf = talloc_array(state, uint8_t, buflen);
+ if (tevent_req_nomem(buf, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ buflen = reparse_data_buffer_marshall(&reparse_buf, buf, buflen);
+ if (buflen != talloc_array_length(buf)) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_create_reparse_point_send(state,
+ ev,
+ cli,
+ newpath,
+ (DATA_BLOB){
+ .data = buf,
+ .length = buflen,
+ });
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_symlink_done, req);
+ return req;
+}
+
+static void cli_symlink_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_symlink_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_symlink_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target,
+ const char *newname, uint32_t flags)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_symlink_send(frame, ev, cli, link_target, newname, flags);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_symlink_recv(req);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_get_reparse_data_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+
+ NTSTATUS get_reparse_status;
+ uint8_t *data;
+ uint32_t datalen;
+};
+
+static void cli_get_reparse_data_opened(struct tevent_req *subreq);
+static void cli_get_reparse_data_done(struct tevent_req *subreq);
+static void cli_get_reparse_data_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_get_reparse_data_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_get_reparse_data_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct cli_get_reparse_data_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ subreq = cli_ntcreate_send(state,
+ ev,
+ cli,
+ fname,
+ 0,
+ FILE_READ_ATTRIBUTES | FILE_READ_EA,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_REPARSE_POINT,
+ SMB2_IMPERSONATION_IMPERSONATION,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_get_reparse_data_opened, req);
+ return req;
+}
+
+static void cli_get_reparse_data_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_get_reparse_data_state *state =
+ tevent_req_data(req, struct cli_get_reparse_data_state);
+ NTSTATUS status;
+
+ status = cli_ntcreate_recv(subreq, &state->fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ subreq = cli_fsctl_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ 65536);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_get_reparse_data_done, req);
+}
+
+static void cli_get_reparse_data_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_get_reparse_data_state *state =
+ tevent_req_data(req, struct cli_get_reparse_data_state);
+ DATA_BLOB out = {
+ .data = NULL,
+ };
+
+ state->get_reparse_status = cli_fsctl_recv(subreq, state, &out);
+ TALLOC_FREE(subreq);
+
+ if (NT_STATUS_IS_OK(state->get_reparse_status)) {
+ state->data = out.data;
+ state->datalen = out.length;
+ }
+
+ subreq = cli_close_send(state, state->ev, state->cli, state->fnum, 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_get_reparse_data_closed, req);
+}
+
+static void cli_get_reparse_data_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_get_reparse_data_state *state =
+ tevent_req_data(req, struct cli_get_reparse_data_state);
+ NTSTATUS status;
+
+ status = cli_close_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (tevent_req_nterror(req, state->get_reparse_status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_get_reparse_data_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_data,
+ uint32_t *_datalen)
+{
+ struct cli_get_reparse_data_state *state =
+ tevent_req_data(req, struct cli_get_reparse_data_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ *_data = talloc_move(mem_ctx, &state->data);
+ *_datalen = state->datalen;
+
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_get_reparse_data(struct cli_state *cli,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_data,
+ uint32_t *_datalen)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_get_reparse_data_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_get_reparse_data_recv(req, mem_ctx, _data, _datalen);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_readlink_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+
+ uint16_t setup[4];
+ uint8_t *data;
+ uint32_t num_data;
+ char *target;
+};
+
+static void cli_readlink_posix1_done(struct tevent_req *subreq);
+static void cli_readlink_got_reparse_data(struct tevent_req *subreq);
+
+struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_readlink_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+
+ if (cli->requested_posix_capabilities != 0) {
+ /*
+ * Only happens for negotiated SMB1 posix caps
+ */
+ subreq = cli_posix_readlink_send(state, ev, cli, fname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_readlink_posix1_done, req);
+ return req;
+ }
+
+ subreq = cli_get_reparse_data_send(state, ev, cli, fname);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req);
+ return req;
+}
+
+static void cli_readlink_posix1_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_readlink_state *state = tevent_req_data(
+ req, struct cli_readlink_state);
+ NTSTATUS status;
+
+ status = cli_posix_readlink_recv(subreq, state, &state->target);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void cli_readlink_got_reparse_data(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_readlink_state *state = tevent_req_data(
+ req, struct cli_readlink_state);
+ NTSTATUS status;
+
+ status = cli_get_reparse_data_recv(subreq,
+ state,
+ &state->data,
+ &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **psubstitute_name, char **pprint_name,
+ uint32_t *pflags)
+{
+ struct cli_readlink_state *state = tevent_req_data(
+ req, struct cli_readlink_state);
+ struct reparse_data_buffer buf = {
+ .tag = 0,
+ };
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->target != NULL) {
+ /*
+ * SMB1 posix version
+ */
+ if (psubstitute_name != NULL) {
+ *psubstitute_name = talloc_move(
+ mem_ctx, &state->target);
+ }
+ if (pprint_name != NULL) {
+ *pprint_name = NULL;
+ }
+ if (pflags != NULL) {
+ *pflags = 0;
+ }
+ return NT_STATUS_OK;
+ }
+
+ status = reparse_data_buffer_parse(state,
+ &buf,
+ state->data,
+ state->num_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (buf.tag != IO_REPARSE_TAG_SYMLINK) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (psubstitute_name != NULL) {
+ *psubstitute_name =
+ talloc_move(mem_ctx, &buf.parsed.lnk.substitute_name);
+ }
+
+ if (pprint_name != NULL) {
+ *pprint_name =
+ talloc_move(mem_ctx, &buf.parsed.lnk.print_name);
+ }
+
+ if (pflags != NULL) {
+ *pflags = buf.parsed.lnk.flags;
+ }
+
+ tevent_req_received(req);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
+ TALLOC_CTX *mem_ctx, char **psubstitute_name,
+ char **pprint_name, uint32_t *pflags)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_readlink_send(frame, ev, cli, fname);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_readlink_recv(req, mem_ctx, psubstitute_name,
+ pprint_name, pflags);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c
new file mode 100644
index 0000000..e5b4c4a
--- /dev/null
+++ b/source3/libsmb/clitrans.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ client transaction calls
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "async_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+struct cli_trans_state {
+ struct cli_state *cli;
+ struct tevent_req *subreq;
+ uint16_t recv_flags2;
+ uint16_t *setup;
+ uint8_t num_setup;
+ uint8_t *param;
+ uint32_t num_param;
+ uint8_t *data;
+ uint32_t num_data;
+};
+
+static void cli_trans_done(struct tevent_req *subreq);
+static bool cli_trans_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_trans_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, uint16_t additional_flags2, uint8_t cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function, int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data)
+{
+ struct tevent_req *req;
+ struct cli_trans_state *state;
+ uint8_t additional_flags = 0;
+ uint8_t clear_flags = 0;
+ uint16_t clear_flags2 = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+
+ state->subreq = smb1cli_trans_send(state, ev,
+ cli->conn, cmd,
+ additional_flags, clear_flags,
+ additional_flags2, clear_flags2,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ pipe_name, fid, function, flags,
+ setup, num_setup, max_setup,
+ param, num_param, max_param,
+ data, num_data, max_data);
+ if (tevent_req_nomem(state->subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->subreq, cli_trans_done, req);
+ tevent_req_set_cancel_fn(req, cli_trans_cancel);
+ return req;
+}
+
+static bool cli_trans_cancel(struct tevent_req *req)
+{
+ struct cli_trans_state *state = tevent_req_data(
+ req, struct cli_trans_state);
+ bool ok;
+
+ ok = tevent_req_cancel(state->subreq);
+ return ok;
+}
+
+static void cli_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_trans_state *state = tevent_req_data(
+ req, struct cli_trans_state);
+ NTSTATUS status;
+
+ status = smb1cli_trans_recv(
+ subreq,
+ state,
+ &state->recv_flags2,
+ &state->setup, 0, &state->num_setup,
+ &state->param, 0, &state->num_param,
+ &state->data, 0, &state->num_data);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint16_t *recv_flags2,
+ uint16_t **setup, uint8_t min_setup,
+ uint8_t *num_setup,
+ uint8_t **param, uint32_t min_param,
+ uint32_t *num_param,
+ uint8_t **data, uint32_t min_data,
+ uint32_t *num_data)
+{
+ struct cli_trans_state *state = tevent_req_data(
+ req, struct cli_trans_state);
+ NTSTATUS status = NT_STATUS_OK;
+ bool map_dos_errors = true;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ goto map_error;
+ }
+
+ if ((state->num_setup < min_setup) ||
+ (state->num_param < min_param) ||
+ (state->num_data < min_data)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (recv_flags2 != NULL) {
+ *recv_flags2 = state->recv_flags2;
+ }
+ if (setup != NULL) {
+ *setup = talloc_move(mem_ctx, &state->setup);
+ *num_setup = state->num_setup;
+ }
+ if (param != NULL) {
+ *param = talloc_move(mem_ctx, &state->param);
+ *num_param = state->num_param;
+ }
+ if (data != NULL) {
+ *data = talloc_move(mem_ctx, &state->data);
+ *num_data = state->num_data;
+ }
+
+map_error:
+ map_dos_errors = state->cli->map_dos_errors;
+ state->cli->raw_status = status;
+
+ if (NT_STATUS_IS_DOS(status) && map_dos_errors) {
+ uint8_t eclass = NT_STATUS_DOS_CLASS(status);
+ uint16_t ecode = NT_STATUS_DOS_CODE(status);
+ /*
+ * TODO: is it really a good idea to do a mapping here?
+ *
+ * The old cli_pull_error() also does it, so I do not change
+ * the behavior yet.
+ */
+ status = dos_to_ntstatus(eclass, ecode);
+ }
+
+ return status;
+}
+
+NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint8_t trans_cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function,
+ int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data,
+ uint16_t *recv_flags2,
+ uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup,
+ uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam,
+ uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_trans_send(
+ frame, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ 0, /* additional_flags2 */
+ trans_cmd, /* cmd */
+ pipe_name, fid, function, flags,
+ setup, num_setup, max_setup,
+ param, num_param, max_param,
+ data, num_data, max_data);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_trans_recv(
+ req, mem_ctx, recv_flags2,
+ rsetup, min_rsetup, num_rsetup,
+ rparam, min_rparam, num_rparam,
+ rdata, min_rdata, num_rdata);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/conncache.c b/source3/libsmb/conncache.c
new file mode 100644
index 0000000..7310b50
--- /dev/null
+++ b/source3/libsmb/conncache.c
@@ -0,0 +1,227 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald (Jerry) Carter 2003
+ Copyright (C) Marc VanHeyningen 2008
+ Copyright (C) Volker Lendecke 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "lib/gencache.h"
+
+/**
+ * @file
+ * Negative connection cache implemented in terms of gencache API
+ *
+ * The negative connection cache stores names of servers which have
+ * been unresponsive so that we don't waste time repeatedly trying
+ * to contact them. It used to use an in-memory linked list, but
+ * this limited its utility to a single process
+ */
+
+
+/**
+ * Marshalls the domain and server name into the key for the gencache
+ * record
+ *
+ * @param[in] domain required
+ * @param[in] server may be a FQDN or an IP address
+ * @return the resulting string, which the caller is responsible for
+ * SAFE_FREE()ing
+ * @retval NULL returned on error
+ */
+static char *negative_conn_cache_keystr(const char *domain, const char *server)
+{
+ char *keystr = NULL;
+
+ if (domain == NULL) {
+ return NULL;
+ }
+ if (server == NULL)
+ server = "";
+
+ keystr = talloc_asprintf(talloc_tos(), "NEG_CONN_CACHE/%s,%s",
+ domain, server);
+ if (keystr == NULL) {
+ DEBUG(0, ("negative_conn_cache_keystr: malloc error\n"));
+ }
+
+ return keystr;
+}
+
+/**
+ * Marshalls the NT status into a printable value field for the gencache
+ * record
+ *
+ * @param[in] status
+ * @return the resulting string, which the caller is responsible for
+ * SAFE_FREE()ing
+ * @retval NULL returned on error
+ */
+static char *negative_conn_cache_valuestr(NTSTATUS status)
+{
+ char *valuestr = NULL;
+
+ valuestr = talloc_asprintf(talloc_tos(), "%x", NT_STATUS_V(status));
+ if (valuestr == NULL) {
+ DEBUG(0, ("negative_conn_cache_valuestr: malloc error\n"));
+ }
+
+ return valuestr;
+}
+
+/**
+ * Un-marshalls the NT status from a printable field for the gencache
+ * record
+ *
+ * @param[in] value The value field from the record
+ * @return the decoded NT status
+ * @retval NT_STATUS_OK returned on error
+ */
+static NTSTATUS negative_conn_cache_valuedecode(const char *value)
+{
+ unsigned int v = NT_STATUS_V(NT_STATUS_INTERNAL_ERROR);
+
+ if (value == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ if (sscanf(value, "%x", &v) != 1) {
+ DEBUG(0, ("negative_conn_cache_valuedecode: unable to parse "
+ "value field '%s'\n", value));
+ }
+ return NT_STATUS(v);
+}
+
+/**
+ * Function passed to gencache_iterate to remove any matching items
+ * from the list
+ *
+ * @param[in] key Key to the record found and to be deleted
+ * @param[in] value Value to the record (ignored)
+ * @param[in] timeout Timeout remaining for the record (ignored)
+ * @param[in] dptr Handle for passing additional data (ignored)
+ */
+static void delete_matches(const char *key, const char *value,
+ time_t timeout, void *dptr)
+{
+ gencache_del(key);
+}
+
+
+/**
+ * Checks for a given domain/server record in the negative cache
+ *
+ * @param[in] domain
+ * @param[in] server may be either a FQDN or an IP address
+ * @return The cached failure status
+ * @retval NT_STATUS_OK returned if no record is found or an error occurs
+ */
+NTSTATUS check_negative_conn_cache( const char *domain, const char *server)
+{
+ NTSTATUS result = NT_STATUS_OK;
+ char *key = NULL;
+ char *value = NULL;
+
+ key = negative_conn_cache_keystr(domain, server);
+ if (key == NULL)
+ goto done;
+
+ if (gencache_get(key, talloc_tos(), &value, NULL))
+ result = negative_conn_cache_valuedecode(value);
+ done:
+ DEBUG(9,("check_negative_conn_cache returning result %d for domain %s "
+ "server %s\n", NT_STATUS_V(result), domain, server));
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+ return result;
+}
+
+/**
+ * Add an entry to the failed connection cache
+ *
+ * @param[in] domain
+ * @param[in] server may be a FQDN or an IP addr in printable form
+ * @param[in] result error to cache; must not be NT_STATUS_OK
+ */
+void add_failed_connection_entry(const char *domain, const char *server,
+ NTSTATUS result)
+{
+ char *key = NULL;
+ char *value = NULL;
+
+ if (NT_STATUS_IS_OK(result)) {
+ /* Nothing failed here */
+ return;
+ }
+
+ key = negative_conn_cache_keystr(domain, server);
+ if (key == NULL) {
+ DEBUG(0, ("add_failed_connection_entry: key creation error\n"));
+ goto done;
+ }
+
+ value = negative_conn_cache_valuestr(result);
+ if (value == NULL) {
+ DEBUG(0, ("add_failed_connection_entry: value creation error\n"));
+ goto done;
+ }
+
+ if (gencache_set(key, value,
+ time(NULL) + FAILED_CONNECTION_CACHE_TIMEOUT))
+ DEBUG(9,("add_failed_connection_entry: added domain %s (%s) "
+ "to failed conn cache\n", domain, server ));
+ else
+ DEBUG(1,("add_failed_connection_entry: failed to add "
+ "domain %s (%s) to failed conn cache\n",
+ domain, server));
+
+ done:
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+ return;
+}
+
+/**
+ * Deletes all records for a specified domain from the negative connection
+ * cache
+ *
+ * @param[in] domain String to match against domain portion of keys, or "*"
+ * to match all domains
+ */
+void flush_negative_conn_cache_for_domain(const char *domain)
+{
+ char *key_pattern = NULL;
+
+ key_pattern = negative_conn_cache_keystr(domain,"*");
+ if (key_pattern == NULL) {
+ DEBUG(0, ("flush_negative_conn_cache_for_domain: "
+ "key creation error\n"));
+ goto done;
+ }
+
+ gencache_iterate(delete_matches, NULL, key_pattern);
+ DEBUG(8, ("flush_negative_conn_cache_for_domain: flushed domain %s\n",
+ domain));
+
+ done:
+ TALLOC_FREE(key_pattern);
+ return;
+}
diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c
new file mode 100644
index 0000000..09a6e66
--- /dev/null
+++ b/source3/libsmb/dsgetdcname.c
@@ -0,0 +1,1274 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dsgetdcname
+
+ Copyright (C) Gerald Carter 2006
+ Copyright (C) Guenther Deschner 2007-2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/dsgetdcname.h"
+#include "libsmb/namequery.h"
+#include "libads/sitename_cache.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "libads/cldap.h"
+#include "lib/addns/dnsquery_srv.h"
+#include "libsmb/clidgram.h"
+#include "lib/gencache.h"
+#include "lib/util/util_net.h"
+
+/* 15 minutes */
+#define DSGETDCNAME_CACHE_TTL 60*15
+
+struct ip_service_name {
+ struct samba_sockaddr sa;
+ const char *hostname;
+};
+
+static NTSTATUS make_dc_info_from_cldap_reply(
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ const struct samba_sockaddr *sa,
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX *r,
+ struct netr_DsRGetDCNameInfo **info);
+
+/****************************************************************
+****************************************************************/
+
+static void debug_dsdcinfo_flags(int lvl, uint32_t flags)
+{
+ DEBUG(lvl,("debug_dsdcinfo_flags: 0x%08x\n\t", flags));
+
+ if (flags & DS_FORCE_REDISCOVERY)
+ DEBUGADD(lvl,("DS_FORCE_REDISCOVERY "));
+ if (flags & 0x00000002)
+ DEBUGADD(lvl,("0x00000002 "));
+ if (flags & 0x00000004)
+ DEBUGADD(lvl,("0x00000004 "));
+ if (flags & 0x00000008)
+ DEBUGADD(lvl,("0x00000008 "));
+ if (flags & DS_DIRECTORY_SERVICE_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_REQUIRED "));
+ if (flags & DS_DIRECTORY_SERVICE_PREFERRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_PREFERRED "));
+ if (flags & DS_GC_SERVER_REQUIRED)
+ DEBUGADD(lvl,("DS_GC_SERVER_REQUIRED "));
+ if (flags & DS_PDC_REQUIRED)
+ DEBUGADD(lvl,("DS_PDC_REQUIRED "));
+ if (flags & DS_BACKGROUND_ONLY)
+ DEBUGADD(lvl,("DS_BACKGROUND_ONLY "));
+ if (flags & DS_IP_REQUIRED)
+ DEBUGADD(lvl,("DS_IP_REQUIRED "));
+ if (flags & DS_KDC_REQUIRED)
+ DEBUGADD(lvl,("DS_KDC_REQUIRED "));
+ if (flags & DS_TIMESERV_REQUIRED)
+ DEBUGADD(lvl,("DS_TIMESERV_REQUIRED "));
+ if (flags & DS_WRITABLE_REQUIRED)
+ DEBUGADD(lvl,("DS_WRITABLE_REQUIRED "));
+ if (flags & DS_GOOD_TIMESERV_PREFERRED)
+ DEBUGADD(lvl,("DS_GOOD_TIMESERV_PREFERRED "));
+ if (flags & DS_AVOID_SELF)
+ DEBUGADD(lvl,("DS_AVOID_SELF "));
+ if (flags & DS_ONLY_LDAP_NEEDED)
+ DEBUGADD(lvl,("DS_ONLY_LDAP_NEEDED "));
+ if (flags & DS_IS_FLAT_NAME)
+ DEBUGADD(lvl,("DS_IS_FLAT_NAME "));
+ if (flags & DS_IS_DNS_NAME)
+ DEBUGADD(lvl,("DS_IS_DNS_NAME "));
+ if (flags & DS_TRY_NEXTCLOSEST_SITE)
+ DEBUGADD(lvl,("DS_TRY_NEXTCLOSEST_SITE "));
+ if (flags & DS_DIRECTORY_SERVICE_6_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_6_REQUIRED "));
+ if (flags & DS_WEB_SERVICE_REQUIRED)
+ DEBUGADD(lvl,("DS_WEB_SERVICE_REQUIRED "));
+ if (flags & DS_DIRECTORY_SERVICE_8_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_8_REQUIRED "));
+ if (flags & DS_DIRECTORY_SERVICE_9_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_9_REQUIRED "));
+ if (flags & DS_DIRECTORY_SERVICE_10_REQUIRED)
+ DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_10_REQUIRED "));
+ if (flags & 0x01000000)
+ DEBUGADD(lvl,("0x01000000 "));
+ if (flags & 0x02000000)
+ DEBUGADD(lvl,("0x02000000 "));
+ if (flags & 0x04000000)
+ DEBUGADD(lvl,("0x04000000 "));
+ if (flags & 0x08000000)
+ DEBUGADD(lvl,("0x08000000 "));
+ if (flags & 0x10000000)
+ DEBUGADD(lvl,("0x10000000 "));
+ if (flags & 0x20000000)
+ DEBUGADD(lvl,("0x20000000 "));
+ if (flags & DS_RETURN_DNS_NAME)
+ DEBUGADD(lvl,("DS_RETURN_DNS_NAME "));
+ if (flags & DS_RETURN_FLAT_NAME)
+ DEBUGADD(lvl,("DS_RETURN_FLAT_NAME "));
+ if (flags)
+ DEBUGADD(lvl,("\n"));
+}
+
+/****************************************************************
+****************************************************************/
+
+static char *dsgetdcname_cache_key(TALLOC_CTX *mem_ctx, const char *domain)
+{
+ if (!domain) {
+ return NULL;
+ }
+
+ return talloc_asprintf_strupper_m(mem_ctx, "DSGETDCNAME/DOMAIN/%s",
+ domain);
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_delete(TALLOC_CTX *mem_ctx,
+ const char *domain_name)
+{
+ char *key;
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!gencache_del(key)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_store(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ DATA_BLOB blob)
+{
+ time_t expire_time;
+ char *key;
+ bool ret = false;
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ expire_time = time(NULL) + DSGETDCNAME_CACHE_TTL;
+
+ ret = gencache_set_data_blob(key, blob, expire_time);
+
+ return ret ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ struct samba_sockaddr *sa,
+ uint32_t nt_version,
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX *r)
+{
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &sa->u.ss);
+
+ /* FIXME */
+ r->sockaddr_size = 0x10; /* the w32 winsock addr size */
+ r->sockaddr.sockaddr_family = 2; /* AF_INET */
+ r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr);
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r,
+ (ndr_push_flags_fn_t)ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ if (r->domain_name) {
+ status = dsgetdcname_cache_store(mem_ctx, r->domain_name,
+ blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (r->client_site) {
+ sitename_store(r->domain_name, r->client_site);
+ }
+ }
+ if (r->dns_domain) {
+ status = dsgetdcname_cache_store(mem_ctx, r->dns_domain, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (r->client_site) {
+ sitename_store(r->dns_domain, r->client_site);
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ done:
+ data_blob_free(&blob);
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static uint32_t get_cldap_reply_server_flags(struct netlogon_samlogon_response *r,
+ uint32_t nt_version)
+{
+ switch (nt_version & 0x0000001f) {
+ case 0:
+ case 1:
+ case 16:
+ case 17:
+ return 0;
+ case 2:
+ case 3:
+ case 18:
+ case 19:
+ return r->data.nt5.server_type;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return r->data.nt5_ex.server_type;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ return r->data.nt5_ex.server_type;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ return r->data.nt5_ex.server_type;
+ case 29:
+ case 30:
+ case 31:
+ return r->data.nt5_ex.server_type;
+ default:
+ return 0;
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cache_fetch(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info_p)
+{
+ char *key;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct netr_DsRGetDCNameInfo *info;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX r;
+ NTSTATUS status;
+
+ key = dsgetdcname_cache_key(mem_ctx, domain_name);
+ if (!key) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!gencache_get_data_blob(key, NULL, &blob, NULL, NULL)) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo);
+ if (!info) {
+ data_blob_free(&blob);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_NETLOGON_SAM_LOGON_RESPONSE_EX);
+
+ data_blob_free(&blob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dsgetdcname_cache_delete(mem_ctx, domain_name);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, NULL,
+ &r, &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, info);
+ }
+
+ /* check flags */
+ if (!check_cldap_reply_required_flags(info->dc_flags, flags)) {
+ DEBUG(10,("invalid flags\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if ((flags & DS_IP_REQUIRED) &&
+ (info->dc_address_type != DS_ADDRESS_TYPE_INET)) {
+ return NT_STATUS_INVALID_PARAMETER_MIX;
+ }
+
+ *info_p = info;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_cached(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+
+ status = dsgetdcname_cache_fetch(mem_ctx, domain_name, domain_guid,
+ flags, info);
+ if (!NT_STATUS_IS_OK(status)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ DEBUG(10,("dsgetdcname_cached: cache fetch failed with: %s\n",
+ nt_errstr(status)));
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ if (flags & DS_BACKGROUND_ONLY) {
+ return status;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ struct netr_DsRGetDCNameInfo *dc_info;
+
+ status = dsgetdcname(mem_ctx, msg_ctx, domain_name,
+ domain_guid, site_name,
+ flags | DS_FORCE_REDISCOVERY,
+ &dc_info);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ *info = dc_info;
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static bool check_allowed_required_flags(uint32_t flags,
+ const char *site_name)
+{
+ uint32_t return_type = flags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME);
+ uint32_t offered_type = flags & (DS_IS_FLAT_NAME|DS_IS_DNS_NAME);
+ uint32_t query_type = flags & (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY);
+
+ /* FIXME: check for DSGETDC_VALID_FLAGS and check for exclusive bits
+ * (DS_PDC_REQUIRED, DS_KDC_REQUIRED, DS_GC_SERVER_REQUIRED) */
+
+ debug_dsdcinfo_flags(10, flags);
+
+ if ((flags & DS_TRY_NEXTCLOSEST_SITE) && site_name) {
+ return false;
+ }
+
+ if (return_type == (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) {
+ return false;
+ }
+
+ if (offered_type == (DS_IS_DNS_NAME|DS_IS_FLAT_NAME)) {
+ return false;
+ }
+
+ if (query_type == (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY)) {
+ return false;
+ }
+
+#if 0
+ if ((flags & DS_RETURN_DNS_NAME) && (!(flags & DS_IP_REQUIRED))) {
+ printf("gd: here5 \n");
+ return false;
+ }
+#endif
+ return true;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS discover_dc_netbios(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name **returned_dclist,
+ size_t *returned_count)
+{
+ NTSTATUS status;
+ enum nbt_name_type name_type = NBT_NAME_LOGON;
+ struct samba_sockaddr *salist = NULL;
+ size_t i;
+ struct ip_service_name *dclist = NULL;
+ size_t count = 0;
+ static const char *resolve_order[] = { "lmhosts", "wins", "bcast", NULL };
+
+ if (flags & DS_PDC_REQUIRED) {
+ name_type = NBT_NAME_PDC;
+ }
+
+ status = internal_resolve_name(mem_ctx,
+ domain_name,
+ name_type,
+ NULL,
+ &salist,
+ &count,
+ resolve_order);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("discover_dc_netbios: failed to find DC\n"));
+ return status;
+ }
+
+ dclist = talloc_zero_array(mem_ctx, struct ip_service_name, count);
+ if (!dclist) {
+ TALLOC_FREE(salist);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ struct ip_service_name *r = &dclist[i];
+
+ print_sockaddr(addr, sizeof(addr),
+ &salist[i].u.ss);
+
+ r->sa = salist[i];
+ r->hostname = talloc_strdup(mem_ctx, addr);
+ if (!r->hostname) {
+ TALLOC_FREE(salist);
+ TALLOC_FREE(dclist);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ }
+
+ TALLOC_FREE(salist);
+
+ *returned_dclist = dclist;
+ *returned_count = count;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct ip_service_name **returned_dclist,
+ size_t *return_count)
+{
+ size_t i;
+ NTSTATUS status;
+ struct dns_rr_srv *dcs = NULL;
+ size_t numdcs = 0;
+ struct ip_service_name *dclist = NULL;
+ size_t ret_count = 0;
+ char *query = NULL;
+
+ if (flags & DS_PDC_REQUIRED) {
+ query = ads_dns_query_string_pdc(mem_ctx, domain_name);
+ } else if (flags & DS_GC_SERVER_REQUIRED) {
+ query = ads_dns_query_string_gcs(mem_ctx, domain_name);
+ } else if (flags & DS_KDC_REQUIRED) {
+ query = ads_dns_query_string_kdcs(mem_ctx, domain_name);
+ } else if (flags & DS_DIRECTORY_SERVICE_REQUIRED) {
+ query = ads_dns_query_string_dcs(mem_ctx, domain_name);
+ } else if (domain_guid) {
+ query = ads_dns_query_string_dcs_guid(
+ mem_ctx, domain_guid, domain_name);
+ } else {
+ query = ads_dns_query_string_dcs(mem_ctx, domain_name);
+ }
+
+ if (query == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ads_dns_query_srv(
+ mem_ctx,
+ lp_get_async_dns_timeout(),
+ site_name,
+ query,
+ &dcs,
+ &numdcs);
+ TALLOC_FREE(query);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (numdcs == 0) {
+ TALLOC_FREE(dcs);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ /* Check for integer wrap. */
+ if (numdcs + numdcs < numdcs) {
+ TALLOC_FREE(dcs);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * We're only returning up to 2 addresses per
+ * DC name, so just allocate size numdcs x 2.
+ */
+
+ dclist = talloc_zero_array(mem_ctx,
+ struct ip_service_name,
+ numdcs * 2);
+ if (!dclist) {
+ TALLOC_FREE(dcs);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * First, copy the SRV record replies that
+ * have IP addresses returned with them.
+ */
+ ret_count = 0;
+ for (i = 0; i < numdcs; i++) {
+ size_t j;
+ bool have_v4_addr = false;
+ bool have_v6_addr = false;
+
+ if (dcs[i].num_ips == 0) {
+ continue;
+ }
+
+ /*
+ * Pick up to 1 address from each address
+ * family (IPv4, IPv6).
+ *
+ * This is different from the previous
+ * code which picked a 'next ip' address
+ * each time, incrementing an index.
+ * Too complex to maintain :-(.
+ */
+ for (j = 0; j < dcs[i].num_ips; j++) {
+ if ((dcs[i].ss_s[j].ss_family == AF_INET && !have_v4_addr) ||
+ (dcs[i].ss_s[j].ss_family == AF_INET6 && !have_v6_addr)) {
+ bool ok;
+ dclist[ret_count].hostname =
+ talloc_strdup(dclist, dcs[i].hostname);
+ ok = sockaddr_storage_to_samba_sockaddr(
+ &dclist[ret_count].sa,
+ &dcs[i].ss_s[j]);
+ if (!ok) {
+ TALLOC_FREE(dcs);
+ TALLOC_FREE(dclist);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret_count++;
+ if (dcs[i].ss_s[j].ss_family == AF_INET) {
+ have_v4_addr = true;
+ } else {
+ have_v6_addr = true;
+ }
+ if (have_v4_addr && have_v6_addr) {
+ break;
+ }
+ }
+ }
+ }
+
+ TALLOC_FREE(dcs);
+
+ if (ret_count == 0) {
+ TALLOC_FREE(dclist);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ *returned_dclist = dclist;
+ *return_count = ret_count;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS make_domain_controller_info(TALLOC_CTX *mem_ctx,
+ const char *dc_unc,
+ const char *dc_address,
+ uint32_t dc_address_type,
+ const struct GUID *domain_guid,
+ const char *domain_name,
+ const char *forest_name,
+ uint32_t flags,
+ const char *dc_site_name,
+ const char *client_site_name,
+ struct netr_DsRGetDCNameInfo **info_out)
+{
+ struct netr_DsRGetDCNameInfo *info;
+
+ info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo);
+ NT_STATUS_HAVE_NO_MEMORY(info);
+
+ if (dc_unc) {
+ if (!(dc_unc[0] == '\\' && dc_unc[1] == '\\')) {
+ info->dc_unc = talloc_asprintf(mem_ctx, "\\\\%s",
+ dc_unc);
+ } else {
+ info->dc_unc = talloc_strdup(mem_ctx, dc_unc);
+ }
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_unc);
+ }
+
+ if (dc_address) {
+ if (!(dc_address[0] == '\\' && dc_address[1] == '\\')) {
+ info->dc_address = talloc_asprintf(mem_ctx, "\\\\%s",
+ dc_address);
+ } else {
+ info->dc_address = talloc_strdup(mem_ctx, dc_address);
+ }
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_address);
+ }
+
+ info->dc_address_type = dc_address_type;
+
+ if (domain_guid) {
+ info->domain_guid = *domain_guid;
+ }
+
+ if (domain_name) {
+ info->domain_name = talloc_strdup(mem_ctx, domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->domain_name);
+ }
+
+ if (forest_name && *forest_name) {
+ info->forest_name = talloc_strdup(mem_ctx, forest_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->forest_name);
+ flags |= DS_DNS_FOREST_ROOT;
+ }
+
+ info->dc_flags = flags;
+
+ if (dc_site_name) {
+ info->dc_site_name = talloc_strdup(mem_ctx, dc_site_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->dc_site_name);
+ }
+
+ if (client_site_name) {
+ info->client_site_name = talloc_strdup(mem_ctx,
+ client_site_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->client_site_name);
+ }
+
+ *info_out = info;
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************
+****************************************************************/
+
+static void map_dc_and_domain_names(uint32_t flags,
+ const char *dc_name,
+ const char *domain_name,
+ const char *dns_dc_name,
+ const char *dns_domain_name,
+ uint32_t *dc_flags,
+ const char **hostname_p,
+ const char **domain_p)
+{
+ switch (flags & 0xf0000000) {
+ case DS_RETURN_FLAT_NAME:
+ if (dc_name && domain_name &&
+ *dc_name && *domain_name) {
+ *hostname_p = dc_name;
+ *domain_p = domain_name;
+ break;
+ }
+
+ FALL_THROUGH;
+ case DS_RETURN_DNS_NAME:
+ default:
+ if (dns_dc_name && dns_domain_name &&
+ *dns_dc_name && *dns_domain_name) {
+ *hostname_p = dns_dc_name;
+ *domain_p = dns_domain_name;
+ *dc_flags |= DS_DNS_DOMAIN | DS_DNS_CONTROLLER;
+ break;
+ }
+ if (dc_name && domain_name &&
+ *dc_name && *domain_name) {
+ *hostname_p = dc_name;
+ *domain_p = domain_name;
+ break;
+ }
+ }
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS make_dc_info_from_cldap_reply(
+ TALLOC_CTX *mem_ctx,
+ uint32_t flags,
+ const struct samba_sockaddr *sa,
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX *r,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ const char *dc_hostname = NULL;
+ const char *dc_domain_name = NULL;
+ const char *dc_address = NULL;
+ const char *dc_forest = NULL;
+ uint32_t dc_address_type = 0;
+ uint32_t dc_flags = 0;
+ struct GUID *dc_domain_guid = NULL;
+ const char *dc_server_site = NULL;
+ const char *dc_client_site = NULL;
+
+ char addr[INET6_ADDRSTRLEN];
+
+ if (sa != NULL) {
+ print_sockaddr(addr, sizeof(addr), &sa->u.ss);
+ dc_address = addr;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ } else {
+ if (r->sockaddr.pdc_ip) {
+ dc_address = r->sockaddr.pdc_ip;
+ dc_address_type = DS_ADDRESS_TYPE_INET;
+ } else {
+ dc_address = r->pdc_name;
+ dc_address_type = DS_ADDRESS_TYPE_NETBIOS;
+ }
+ }
+
+ map_dc_and_domain_names(flags,
+ r->pdc_name,
+ r->domain_name,
+ r->pdc_dns_name,
+ r->dns_domain,
+ &dc_flags,
+ &dc_hostname,
+ &dc_domain_name);
+
+ dc_flags |= r->server_type;
+ dc_forest = r->forest;
+ dc_domain_guid = &r->domain_uuid;
+ dc_server_site = r->server_site;
+ dc_client_site = r->client_site;
+
+ return make_domain_controller_info(mem_ctx,
+ dc_hostname,
+ dc_address,
+ dc_address_type,
+ dc_domain_guid,
+ dc_domain_name,
+ dc_forest,
+ dc_flags,
+ dc_server_site,
+ dc_client_site,
+ info);
+}
+
+/****************************************************************
+****************************************************************/
+
+static uint32_t map_ds_flags_to_nt_version(uint32_t flags)
+{
+ uint32_t nt_version = 0;
+
+ if (flags & DS_PDC_REQUIRED) {
+ nt_version |= NETLOGON_NT_VERSION_PDC;
+ }
+
+ if (flags & DS_GC_SERVER_REQUIRED) {
+ nt_version |= NETLOGON_NT_VERSION_GC;
+ }
+
+ if (flags & DS_TRY_NEXTCLOSEST_SITE) {
+ nt_version |= NETLOGON_NT_VERSION_WITH_CLOSEST_SITE;
+ }
+
+ if (flags & DS_IP_REQUIRED) {
+ nt_version |= NETLOGON_NT_VERSION_IP;
+ }
+
+ return nt_version;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS process_dc_dns(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name *dclist,
+ size_t num_dcs,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ size_t i = 0;
+ bool valid_dc = false;
+ struct netlogon_samlogon_response *r = NULL;
+ uint32_t nt_version = NETLOGON_NT_VERSION_5 |
+ NETLOGON_NT_VERSION_5EX;
+ uint32_t ret_flags = 0;
+ NTSTATUS status;
+
+ nt_version |= map_ds_flags_to_nt_version(flags);
+
+ for (i=0; i<num_dcs; i++) {
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &dclist[i].sa.u.ss);
+
+ DEBUG(10,("LDAP ping to %s (%s)\n", dclist[i].hostname, addr));
+
+ if (ads_cldap_netlogon(mem_ctx, &dclist[i].sa.u.ss,
+ domain_name,
+ nt_version,
+ &r))
+ {
+ nt_version = r->ntver;
+ ret_flags = get_cldap_reply_server_flags(r, nt_version);
+
+ if (check_cldap_reply_required_flags(ret_flags, flags)) {
+ valid_dc = true;
+ break;
+ }
+ }
+
+ continue;
+ }
+
+ if (!valid_dc) {
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].sa,
+ &r->data.nt5_ex, info);
+ if (NT_STATUS_IS_OK(status)) {
+ return store_cldap_reply(mem_ctx, flags, &dclist[i].sa,
+ nt_version, &r->data.nt5_ex);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ uint32_t flags,
+ struct ip_service_name *dclist,
+ size_t num_dcs,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ enum nbt_name_type name_type = NBT_NAME_LOGON;
+ NTSTATUS status;
+ size_t i;
+ const char *dc_name = NULL;
+ fstring tmp_dc_name;
+ struct netlogon_samlogon_response *r = NULL;
+ bool store_cache = false;
+ uint32_t nt_version = NETLOGON_NT_VERSION_1 |
+ NETLOGON_NT_VERSION_5 |
+ NETLOGON_NT_VERSION_5EX_WITH_IP;
+ size_t len = strlen(lp_netbios_name());
+ char my_acct_name[len+2];
+
+ if (msg_ctx == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (flags & DS_PDC_REQUIRED) {
+ name_type = NBT_NAME_PDC;
+ }
+
+ nt_version |= map_ds_flags_to_nt_version(flags);
+
+ snprintf(my_acct_name,
+ sizeof(my_acct_name),
+ "%s$",
+ lp_netbios_name());
+
+ DEBUG(10,("process_dc_netbios\n"));
+
+ for (i=0; i<num_dcs; i++) {
+ uint16_t val;
+
+ generate_random_buffer((uint8_t *)&val, 2);
+
+ status = nbt_getdc(msg_ctx, 10, &dclist[i].sa.u.ss, domain_name,
+ NULL, my_acct_name, ACB_WSTRUST, nt_version,
+ mem_ctx, &nt_version, &dc_name, &r);
+ if (NT_STATUS_IS_OK(status)) {
+ store_cache = true;
+ namecache_store(dc_name,
+ NBT_NAME_SERVER,
+ 1,
+ &dclist[i].sa);
+ goto make_reply;
+ }
+
+ if (name_status_find(domain_name,
+ name_type,
+ NBT_NAME_SERVER,
+ &dclist[i].sa.u.ss,
+ tmp_dc_name))
+ {
+ struct NETLOGON_SAM_LOGON_RESPONSE_NT40 logon1;
+
+ r = talloc_zero(mem_ctx, struct netlogon_samlogon_response);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ ZERO_STRUCT(logon1);
+
+ nt_version = NETLOGON_NT_VERSION_1;
+
+ logon1.nt_version = nt_version;
+ logon1.pdc_name = tmp_dc_name;
+ logon1.domain_name = talloc_strdup_upper(mem_ctx, domain_name);
+ NT_STATUS_HAVE_NO_MEMORY(logon1.domain_name);
+
+ r->data.nt4 = logon1;
+ r->ntver = nt_version;
+
+ map_netlogon_samlogon_response(r);
+
+ namecache_store(tmp_dc_name,
+ NBT_NAME_SERVER,
+ 1,
+ &dclist[i].sa);
+
+ goto make_reply;
+ }
+ }
+
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+
+ make_reply:
+
+ status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].sa,
+ &r->data.nt5_ex, info);
+ if (NT_STATUS_IS_OK(status) && store_cache) {
+ return store_cldap_reply(mem_ctx, flags, &dclist[i].sa,
+ nt_version, &r->data.nt5_ex);
+ }
+
+ return status;
+}
+
+/****************************************************************
+****************************************************************/
+
+static NTSTATUS dsgetdcname_rediscover(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ uint32_t flags,
+ const char *site_name,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+ struct ip_service_name *dclist = NULL;
+ size_t num_dcs = 0;
+
+ DEBUG(10,("dsgetdcname_rediscover\n"));
+
+ if (flags & DS_IS_FLAT_NAME) {
+
+ if (lp_disable_netbios()) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ status = discover_dc_netbios(mem_ctx, domain_name, flags,
+ &dclist, &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags,
+ dclist, num_dcs, info);
+ }
+
+ if (flags & DS_IS_DNS_NAME) {
+
+ status = discover_dc_dns(mem_ctx, domain_name, domain_guid,
+ flags, site_name, &dclist, &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_dns(mem_ctx, domain_name, flags,
+ dclist, num_dcs, info);
+ }
+
+ status = discover_dc_dns(mem_ctx, domain_name, domain_guid, flags,
+ site_name, &dclist, &num_dcs);
+
+ if (NT_STATUS_IS_OK(status) && num_dcs != 0) {
+
+ status = process_dc_dns(mem_ctx, domain_name, flags, dclist,
+ num_dcs, info);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (lp_disable_netbios()) {
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ status = discover_dc_netbios(mem_ctx, domain_name, flags, &dclist,
+ &num_dcs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, dclist,
+ num_dcs, info);
+}
+
+static bool is_closest_site(struct netr_DsRGetDCNameInfo *info)
+{
+ if (info->dc_flags & DS_SERVER_CLOSEST) {
+ return true;
+ }
+
+ if (!info->client_site_name) {
+ return true;
+ }
+
+ if (!info->dc_site_name) {
+ return false;
+ }
+
+ if (strcmp(info->client_site_name, info->dc_site_name) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/********************************************************************
+ Internal dsgetdcname.
+********************************************************************/
+
+static NTSTATUS dsgetdcname_internal(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+ struct netr_DsRGetDCNameInfo *myinfo = NULL;
+ bool first = true;
+ struct netr_DsRGetDCNameInfo *first_info = NULL;
+
+ DEBUG(10,("dsgetdcname_internal: domain_name: %s, "
+ "domain_guid: %s, site_name: %s, flags: 0x%08x\n",
+ domain_name,
+ domain_guid ? GUID_string(mem_ctx, domain_guid) : "(null)",
+ site_name ? site_name : "(null)", flags));
+
+ *info = NULL;
+
+ if (!check_allowed_required_flags(flags, site_name)) {
+ DEBUG(0,("invalid flags specified\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (flags & DS_FORCE_REDISCOVERY) {
+ goto rediscover;
+ }
+
+ status = dsgetdcname_cached(mem_ctx, msg_ctx, domain_name, domain_guid,
+ flags, site_name, &myinfo);
+ if (NT_STATUS_IS_OK(status)) {
+ *info = myinfo;
+ goto done;
+ }
+
+ if (flags & DS_BACKGROUND_ONLY) {
+ goto done;
+ }
+
+ rediscover:
+ status = dsgetdcname_rediscover(mem_ctx, msg_ctx, domain_name,
+ domain_guid, flags, site_name,
+ &myinfo);
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!first) {
+ *info = first_info;
+ return NT_STATUS_OK;
+ }
+ return status;
+ }
+
+ if (!first) {
+ TALLOC_FREE(first_info);
+ } else if (!is_closest_site(myinfo)) {
+ first = false;
+ first_info = myinfo;
+ /* TODO: may use the next_closest_site here */
+ site_name = myinfo->client_site_name;
+ goto rediscover;
+ }
+
+ *info = myinfo;
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ dsgetdcname.
+
+ This will be the only public function here.
+********************************************************************/
+
+NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+ const char *query_site = NULL;
+ char *ptr_to_free = NULL;
+ bool retry_query_with_null = false;
+
+ if ((site_name == NULL) || (site_name[0] == '\0')) {
+ ptr_to_free = sitename_fetch(mem_ctx, domain_name);
+ if (ptr_to_free != NULL) {
+ retry_query_with_null = true;
+ }
+ query_site = ptr_to_free;
+ } else {
+ query_site = site_name;
+ }
+
+ status = dsgetdcname_internal(mem_ctx,
+ msg_ctx,
+ domain_name,
+ domain_guid,
+ query_site,
+ flags,
+ info);
+
+ TALLOC_FREE(ptr_to_free);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
+ return status;
+ }
+
+ /* Should we try again with site_name == NULL ? */
+ if (retry_query_with_null) {
+ status = dsgetdcname_internal(mem_ctx,
+ msg_ctx,
+ domain_name,
+ domain_guid,
+ NULL,
+ flags,
+ info);
+ }
+
+ return status;
+}
+
+NTSTATUS dsgetonedcname(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const char *dcname,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info)
+{
+ NTSTATUS status;
+ struct sockaddr_storage *addrs;
+ unsigned int num_addrs, i;
+ const char *hostname = strip_hostname(dcname);
+
+ status = resolve_name_list(mem_ctx, hostname, 0x20,
+ &addrs, &num_addrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (i = 0; i < num_addrs; i++) {
+
+ bool ok;
+ struct ip_service_name dclist;
+
+ dclist.hostname = hostname;
+ ok = sockaddr_storage_to_samba_sockaddr(&dclist.sa, &addrs[i]);
+ if (!ok) {
+ TALLOC_FREE(addrs);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ }
+
+ status = process_dc_dns(mem_ctx, domain_name, flags,
+ &dclist, 1, info);
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(addrs);
+ return NT_STATUS_OK;
+ }
+
+ if (lp_disable_netbios()) {
+ continue;
+ }
+
+ status = process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags,
+ &dclist, 1, info);
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(addrs);
+ return NT_STATUS_OK;
+ }
+ }
+
+ TALLOC_FREE(addrs);
+ return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+}
diff --git a/source3/libsmb/dsgetdcname.h b/source3/libsmb/dsgetdcname.h
new file mode 100644
index 0000000..241b721
--- /dev/null
+++ b/source3/libsmb/dsgetdcname.h
@@ -0,0 +1,43 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMB_DSGETDCNAME_H__
+#define __LIBSMB_DSGETDCNAME_H__
+
+#include "replace.h"
+#include <talloc.h>
+#include "librpc/gen_ndr/misc.h"
+
+struct netr_DsRGetDCNameInfo;
+struct messaging_context;
+
+NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const struct GUID *domain_guid,
+ const char *site_name,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info);
+
+NTSTATUS dsgetonedcname(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg_ctx,
+ const char *domain_name,
+ const char *dcname,
+ uint32_t flags,
+ struct netr_DsRGetDCNameInfo **info);
+#endif
diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c
new file mode 100644
index 0000000..fd7d6a1
--- /dev/null
+++ b/source3/libsmb/errormap.c
@@ -0,0 +1,295 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+/* dos -> nt status error map */
+static const struct {
+ uint8_t dos_class;
+ uint32_t dos_code;
+ NTSTATUS ntstatus;
+} dos_to_ntstatus_map[] = {
+ {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE},
+ {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE},
+ {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRDOS, ERRbadaccess, NT_STATUS_ACCESS_DENIED},
+ {ERRDOS, ERRbaddata, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRDOS, ERRremcd, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES},
+ {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRDOS, 23, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 24, NT_STATUS_DATA_ERROR},
+ {ERRDOS, 26, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRDOS, 28, NT_STATUS(0x8000000e)},
+ {ERRDOS, 31, NT_STATUS_UNSUCCESSFUL},
+ {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRDOS, 34, NT_STATUS_WRONG_VOLUME},
+ {ERRDOS, 38, NT_STATUS_END_OF_FILE},
+ {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRDOS, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRDOS, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRDOS, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRDOS, 111, STATUS_MORE_ENTRIES},
+ {ERRDOS, 112, NT_STATUS_DISK_FULL},
+ {ERRDOS, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRDOS, ERRunknownlevel, NT_STATUS_INVALID_LEVEL},
+ {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRDOS, 158, NT_STATUS_NOT_LOCKED},
+ {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRDOS, 170, NT_STATUS(0x80000011)},
+ {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRDOS, 203, NT_STATUS(0xc0000100)},
+ {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING},
+ {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED},
+ {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRDOS, 254, NT_STATUS(0x80000013)},
+ {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRDOS, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRDOS, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRDOS, 299, NT_STATUS(0x8000000d)},
+ {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRDOS, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRDOS, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRDOS, 995, NT_STATUS_CANCELLED},
+ {ERRDOS, 997, NT_STATUS(0x00000103)},
+ {ERRDOS, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRDOS, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRDOS, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRDOS, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRDOS, 1006, NT_STATUS_FILE_INVALID},
+ {ERRDOS, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRDOS, 1008, NT_STATUS_NO_TOKEN},
+ {ERRDOS, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRDOS, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRDOS, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRDOS, 1018, NT_STATUS_KEY_DELETED},
+ {ERRDOS, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRDOS, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRDOS, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRDOS, 1022, NT_STATUS(0x0000010c)},
+ {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD},
+ {ERRSRV, ERRbaduid, NT_STATUS_USER_SESSION_DELETED},
+ {ERRSRV, ERRbadtype, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRaccess, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRinvnid, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRSRV, ERRinvnetname, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRSRV, ERRqfull, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRSRV, ERRqtoobig, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRSRV, ERRinvpfid, NT_STATUS_PRINT_CANCELLED},
+ {ERRSRV, ERRsmbcmd, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRSRV, ERRbadpermits, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRSRV, ERRpaused, NT_STATUS_SHARING_PAUSED},
+ {ERRSRV, ERRmsgoff, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRnoroom, NT_STATUS_DISK_FULL},
+ {ERRSRV, ERRnoresource, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRSRV, ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRSRV, ERRunknownsmb, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRSRV, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRSRV, 206, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 1, NT_STATUS_NOT_IMPLEMENTED},
+ {ERRHRD, 2, NT_STATUS_NO_SUCH_DEVICE},
+ {ERRHRD, 3, NT_STATUS_OBJECT_PATH_NOT_FOUND},
+ {ERRHRD, 4, NT_STATUS_TOO_MANY_OPENED_FILES},
+ {ERRHRD, 5, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 6, NT_STATUS_INVALID_HANDLE},
+ {ERRHRD, 8, NT_STATUS_INSUFFICIENT_RESOURCES},
+ {ERRHRD, 12, NT_STATUS_INVALID_LOCK_SEQUENCE},
+ {ERRHRD, 13, NT_STATUS_DATA_ERROR},
+ {ERRHRD, 14, NT_STATUS_SECTION_NOT_EXTENDED},
+ {ERRHRD, 16, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 17, NT_STATUS_NOT_SAME_DEVICE},
+ {ERRHRD, 18, NT_STATUS(0x80000006)},
+ {ERRHRD, ERRnowrite, NT_STATUS_MEDIA_WRITE_PROTECTED},
+ {ERRHRD, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE},
+ {ERRHRD, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE},
+ {ERRHRD, ERRdata, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadreq, NT_STATUS_DATA_ERROR},
+ {ERRHRD, ERRbadmedia, NT_STATUS_DISK_CORRUPT_ERROR},
+ {ERRHRD, ERRbadsector, NT_STATUS_NONEXISTENT_SECTOR},
+ {ERRHRD, ERRnopaper, NT_STATUS(0x8000000e)},
+ {ERRHRD, ERRgeneral, NT_STATUS_UNSUCCESSFUL},
+ {ERRHRD, ERRbadshare, NT_STATUS_SHARING_VIOLATION},
+ {ERRHRD, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT},
+ {ERRHRD, ERRwrongdisk, NT_STATUS_WRONG_VOLUME},
+ {ERRHRD, 38, NT_STATUS_END_OF_FILE},
+ {ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL},
+ {ERRHRD, 50, NT_STATUS_CTL_FILE_NOT_SUPPORTED},
+ {ERRHRD, 51, NT_STATUS_REMOTE_NOT_LISTENING},
+ {ERRHRD, 52, NT_STATUS_DUPLICATE_NAME},
+ {ERRHRD, 53, NT_STATUS_BAD_NETWORK_PATH},
+ {ERRHRD, 54, NT_STATUS_NETWORK_BUSY},
+ {ERRHRD, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST},
+ {ERRHRD, 56, NT_STATUS_TOO_MANY_COMMANDS},
+ {ERRHRD, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR},
+ {ERRHRD, 58, NT_STATUS_INVALID_NETWORK_RESPONSE},
+ {ERRHRD, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR},
+ {ERRHRD, 60, NT_STATUS_BAD_REMOTE_ADAPTER},
+ {ERRHRD, 61, NT_STATUS_PRINT_QUEUE_FULL},
+ {ERRHRD, 62, NT_STATUS_NO_SPOOL_SPACE},
+ {ERRHRD, 63, NT_STATUS_PRINT_CANCELLED},
+ {ERRHRD, 64, NT_STATUS_NETWORK_NAME_DELETED},
+ {ERRHRD, 65, NT_STATUS_NETWORK_ACCESS_DENIED},
+ {ERRHRD, 66, NT_STATUS_BAD_DEVICE_TYPE},
+ {ERRHRD, 67, NT_STATUS_BAD_NETWORK_NAME},
+ {ERRHRD, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED},
+ {ERRHRD, 69, NT_STATUS_TOO_MANY_SESSIONS},
+ {ERRHRD, 70, NT_STATUS_SHARING_PAUSED},
+ {ERRHRD, 71, NT_STATUS_REQUEST_NOT_ACCEPTED},
+ {ERRHRD, 72, NT_STATUS_REDIRECTOR_PAUSED},
+ {ERRHRD, 80, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 86, NT_STATUS_WRONG_PASSWORD},
+ {ERRHRD, 87, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 88, NT_STATUS_NET_WRITE_FAULT},
+ {ERRHRD, 109, NT_STATUS_PIPE_BROKEN},
+ {ERRHRD, 111, STATUS_MORE_ENTRIES},
+ {ERRHRD, 112, NT_STATUS_DISK_FULL},
+ {ERRHRD, 121, NT_STATUS_IO_TIMEOUT},
+ {ERRHRD, 122, NT_STATUS_BUFFER_TOO_SMALL},
+ {ERRHRD, 123, NT_STATUS_OBJECT_NAME_INVALID},
+ {ERRHRD, 124, NT_STATUS_INVALID_LEVEL},
+ {ERRHRD, 126, NT_STATUS_DLL_NOT_FOUND},
+ {ERRHRD, 127, NT_STATUS_PROCEDURE_NOT_FOUND},
+ {ERRHRD, 145, NT_STATUS_DIRECTORY_NOT_EMPTY},
+ {ERRHRD, 154, NT_STATUS_INVALID_VOLUME_LABEL},
+ {ERRHRD, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED},
+ {ERRHRD, 158, NT_STATUS_NOT_LOCKED},
+ {ERRHRD, 161, NT_STATUS_OBJECT_PATH_INVALID},
+ {ERRHRD, 170, NT_STATUS(0x80000011)},
+ {ERRHRD, 182, NT_STATUS_ORDINAL_NOT_FOUND},
+ {ERRHRD, 183, NT_STATUS_OBJECT_NAME_COLLISION},
+ {ERRHRD, 193, NT_STATUS_BAD_INITIAL_PC},
+ {ERRHRD, 203, NT_STATUS(0xc0000100)},
+ {ERRHRD, 206, NT_STATUS_NAME_TOO_LONG},
+ {ERRHRD, 230, NT_STATUS_INVALID_INFO_CLASS},
+ {ERRHRD, 231, NT_STATUS_INSTANCE_NOT_AVAILABLE},
+ {ERRHRD, 232, NT_STATUS_PIPE_CLOSING},
+ {ERRHRD, 233, NT_STATUS_PIPE_DISCONNECTED},
+ {ERRHRD, 234, STATUS_MORE_ENTRIES},
+ {ERRHRD, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED},
+ {ERRHRD, 254, NT_STATUS(0x80000013)},
+ {ERRHRD, 255, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 259, NT_STATUS_GUIDS_EXHAUSTED},
+ {ERRHRD, 267, NT_STATUS_NOT_A_DIRECTORY},
+ {ERRHRD, 275, NT_STATUS_EA_TOO_LARGE},
+ {ERRHRD, 276, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 277, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, 278, NT_STATUS_NONEXISTENT_EA_ENTRY},
+ {ERRHRD, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED},
+ {ERRHRD, 288, NT_STATUS_MUTANT_NOT_OWNED},
+ {ERRHRD, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED},
+ {ERRHRD, 299, NT_STATUS(0x8000000d)},
+ {ERRHRD, 300, NT_STATUS_OPLOCK_NOT_GRANTED},
+ {ERRHRD, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL},
+ {ERRHRD, 487, NT_STATUS_CONFLICTING_ADDRESSES},
+ {ERRHRD, 534, NT_STATUS_INTEGER_OVERFLOW},
+ {ERRHRD, 535, NT_STATUS_PIPE_CONNECTED},
+ {ERRHRD, 536, NT_STATUS_PIPE_LISTENING},
+ {ERRHRD, 995, NT_STATUS_CANCELLED},
+ {ERRHRD, 997, NT_STATUS(0x00000103)},
+ {ERRHRD, 998, NT_STATUS_ACCESS_VIOLATION},
+ {ERRHRD, 999, NT_STATUS_IN_PAGE_ERROR},
+ {ERRHRD, 1001, NT_STATUS_BAD_INITIAL_STACK},
+ {ERRHRD, 1005, NT_STATUS_UNRECOGNIZED_VOLUME},
+ {ERRHRD, 1006, NT_STATUS_FILE_INVALID},
+ {ERRHRD, 1007, NT_STATUS_FULLSCREEN_MODE},
+ {ERRHRD, 1008, NT_STATUS_NO_TOKEN},
+ {ERRHRD, 1009, NT_STATUS_REGISTRY_CORRUPT},
+ {ERRHRD, 1016, NT_STATUS_REGISTRY_IO_FAILED},
+ {ERRHRD, 1017, NT_STATUS_NOT_REGISTRY_FILE},
+ {ERRHRD, 1018, NT_STATUS_KEY_DELETED},
+ {ERRHRD, 1019, NT_STATUS_NO_LOG_SPACE},
+ {ERRHRD, 1020, NT_STATUS_KEY_HAS_CHILDREN},
+ {ERRHRD, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE},
+ {ERRHRD, 1022, NT_STATUS(0x0000010c)},
+};
+
+/*****************************************************************************
+convert a dos eclas/ecode to a NT status32 code
+ *****************************************************************************/
+NTSTATUS dos_to_ntstatus(uint8_t eclass, uint32_t ecode)
+{
+ size_t i;
+ if (eclass == 0) return NT_STATUS_OK;
+ for (i=0; i < ARRAY_SIZE(dos_to_ntstatus_map); i++) {
+ if (eclass == dos_to_ntstatus_map[i].dos_class &&
+ ecode == dos_to_ntstatus_map[i].dos_code) {
+ return dos_to_ntstatus_map[i].ntstatus;
+ }
+ }
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source3/libsmb/errormap_wbc.c b/source3/libsmb/errormap_wbc.c
new file mode 100644
index 0000000..ad523d6
--- /dev/null
+++ b/source3/libsmb/errormap_wbc.c
@@ -0,0 +1,62 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "nsswitch/libwbclient/wbclient.h"
+
+/*******************************************************************************
+ Map between wbcErr and NT status.
+*******************************************************************************/
+
+static const struct {
+ wbcErr wbc_err;
+ NTSTATUS nt_status;
+} wbcErr_ntstatus_map[] = {
+ { WBC_ERR_SUCCESS, NT_STATUS_OK },
+ { WBC_ERR_NOT_IMPLEMENTED, NT_STATUS_NOT_IMPLEMENTED },
+ { WBC_ERR_UNKNOWN_FAILURE, NT_STATUS_UNSUCCESSFUL },
+ { WBC_ERR_NO_MEMORY, NT_STATUS_NO_MEMORY },
+ { WBC_ERR_INVALID_SID, NT_STATUS_INVALID_SID },
+ { WBC_ERR_INVALID_PARAM, NT_STATUS_INVALID_PARAMETER },
+ { WBC_ERR_WINBIND_NOT_AVAILABLE, NT_STATUS_SERVER_DISABLED },
+ { WBC_ERR_DOMAIN_NOT_FOUND, NT_STATUS_NO_SUCH_DOMAIN },
+ { WBC_ERR_INVALID_RESPONSE, NT_STATUS_INVALID_NETWORK_RESPONSE },
+ { WBC_ERR_NSS_ERROR, NT_STATUS_INTERNAL_ERROR },
+ { WBC_ERR_AUTH_ERROR, NT_STATUS_LOGON_FAILURE },
+ { WBC_ERR_UNKNOWN_USER, NT_STATUS_NO_SUCH_USER },
+ { WBC_ERR_UNKNOWN_GROUP, NT_STATUS_NO_SUCH_GROUP },
+ { WBC_ERR_PWD_CHANGE_FAILED, NT_STATUS_PASSWORD_RESTRICTION }
+};
+
+NTSTATUS map_nt_error_from_wbcErr(wbcErr wbc_err)
+{
+ int i;
+
+ /* Look through list */
+ for (i=0;i<ARRAY_SIZE(wbcErr_ntstatus_map);i++) {
+ if (wbcErr_ntstatus_map[i].wbc_err == wbc_err) {
+ return wbcErr_ntstatus_map[i].nt_status;
+ }
+ }
+
+ /* Default return */
+ return NT_STATUS_UNSUCCESSFUL;
+}
diff --git a/source3/libsmb/errormap_wbc.h b/source3/libsmb/errormap_wbc.h
new file mode 100644
index 0000000..6323dee
--- /dev/null
+++ b/source3/libsmb/errormap_wbc.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * error mapping functions
+ * Copyright (C) Andrew Tridgell 2001
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Tim Potter 2000
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LIBSMB_ERRORMAP_WBC_H_
+#define _LIBSMB_ERRORMAP_WBC_H_
+
+/* The following definitions come from libsmb/errormap_wbc.c */
+
+NTSTATUS map_nt_error_from_wbcErr(wbcErr wbc_err);
+
+#endif /* _LIBSMB_ERRORMAP_WBC_H_ */
diff --git a/source3/libsmb/libsmb.h b/source3/libsmb/libsmb.h
new file mode 100644
index 0000000..6df06ae
--- /dev/null
+++ b/source3/libsmb/libsmb.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 1992-1998,2001
+ Copyright (C) Jeremy Allison 1998
+ Copyright (C) Remus Koos 2001
+ Copyright (C) Andrew Bartlett 2001
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB_LIBSMB_H
+#define _LIBSMB_LIBSMB_H
+
+#include "client.h"
+#include "libads/ads_status.h"
+#include "libsmb/proto.h"
+#include "libsmb/cli_smb2_fnum.h"
+
+#endif /* _LIBSMB_LIBSMB_H */
diff --git a/source3/libsmb/libsmb_cache.c b/source3/libsmb/libsmb_cache.c
new file mode 100644
index 0000000..91292b1
--- /dev/null
+++ b/source3/libsmb/libsmb_cache.c
@@ -0,0 +1,242 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (server cache)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+/*
+ * Structure we use if internal caching mechanism is used
+ * nothing fancy here.
+ */
+struct smbc_server_cache {
+ char *server_name;
+ char *share_name;
+ char *workgroup;
+ char *username;
+ SMBCSRV *server;
+
+ struct smbc_server_cache *next, *prev;
+};
+
+
+
+/*
+ * Add a new connection to the server cache.
+ * This function is only used if the external cache is not enabled
+ */
+int
+SMBC_add_cached_server(SMBCCTX * context,
+ SMBCSRV * newsrv,
+ const char * server,
+ const char * share,
+ const char * workgroup,
+ const char * username)
+{
+ struct smbc_server_cache * srvcache = NULL;
+
+ srvcache = SMB_CALLOC_ARRAY(struct smbc_server_cache, 1);
+ if (srvcache == NULL) {
+ DEBUG(3, ("Not enough space for server cache allocation\n"));
+ errno = ENOMEM;
+ return 1;
+ }
+
+ srvcache->server = newsrv;
+
+ srvcache->server_name = SMB_STRDUP(server);
+ if (!srvcache->server_name) {
+ goto nomem;
+ }
+
+ srvcache->share_name = SMB_STRDUP(share);
+ if (!srvcache->share_name) {
+ goto nomem;
+ }
+
+ srvcache->workgroup = SMB_STRDUP(workgroup);
+ if (!srvcache->workgroup) {
+ goto nomem;
+ }
+
+ srvcache->username = SMB_STRDUP(username);
+ if (!srvcache->username) {
+ goto nomem;
+ }
+
+ DLIST_ADD(context->internal->server_cache, srvcache);
+ return 0;
+
+nomem:
+ SAFE_FREE(srvcache->server_name);
+ SAFE_FREE(srvcache->share_name);
+ SAFE_FREE(srvcache->workgroup);
+ SAFE_FREE(srvcache->username);
+ SAFE_FREE(srvcache);
+ errno = ENOMEM;
+
+ return 1;
+}
+
+
+
+/*
+ * Search the server cache for a server
+ * returns server handle on success, NULL on error (not found)
+ * This function is only used if the external cache is not enabled
+ */
+SMBCSRV *
+SMBC_get_cached_server(SMBCCTX * context,
+ const char * server,
+ const char * share,
+ const char * workgroup,
+ const char * user)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ /* Search the cache lines */
+ for (srv = context->internal->server_cache; srv; srv = srv->next) {
+
+ if (strcmp(server,srv->server_name) == 0 &&
+ strcmp(workgroup,srv->workgroup) == 0 &&
+ strcmp(user, srv->username) == 0) {
+
+ /* If the share name matches, we're cool */
+ if (strcmp(share, srv->share_name) == 0) {
+ return srv->server;
+ }
+
+ /*
+ * We only return an empty share name or the attribute
+ * server on an exact match (which would have been
+ * caught above).
+ */
+ if (*share == '\0' || strcmp(share, "*IPC$") == 0)
+ continue;
+
+ /*
+ * Never return an empty share name or the attribute
+ * server if it wasn't what was requested.
+ */
+ if (*srv->share_name == '\0' ||
+ strcmp(srv->share_name, "*IPC$") == 0)
+ continue;
+
+ /*
+ * If we're only allowing one share per server, then
+ * a connection to the server (other than the
+ * attribute server connection) is cool.
+ */
+ if (smbc_getOptionOneSharePerServer(context)) {
+ NTSTATUS status;
+ /*
+ * The currently connected share name
+ * doesn't match the requested share, so
+ * disconnect from the current share.
+ */
+ status = cli_tdis(srv->server->cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Sigh. Couldn't disconnect. */
+ cli_shutdown(srv->server->cli);
+ srv->server->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context, srv->server);
+ continue;
+ }
+
+ /*
+ * Save the new share name. We've
+ * disconnected from the old share, and are
+ * about to connect to the new one.
+ */
+ SAFE_FREE(srv->share_name);
+ srv->share_name = SMB_STRDUP(share);
+ if (!srv->share_name) {
+ /* Out of memory. */
+ cli_shutdown(srv->server->cli);
+ srv->server->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context, srv->server);
+ continue;
+ }
+
+ return srv->server;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Search the server cache for a server and remove it
+ * returns 0 on success
+ * This function is only used if the external cache is not enabled
+ */
+int
+SMBC_remove_cached_server(SMBCCTX * context,
+ SMBCSRV * server)
+{
+ struct smbc_server_cache * srv = NULL;
+
+ for (srv = context->internal->server_cache; srv; srv = srv->next) {
+ if (server == srv->server) {
+
+ /* remove this sucker */
+ DLIST_REMOVE(context->internal->server_cache, srv);
+ SAFE_FREE(srv->server_name);
+ SAFE_FREE(srv->share_name);
+ SAFE_FREE(srv->workgroup);
+ SAFE_FREE(srv->username);
+ SAFE_FREE(srv);
+ return 0;
+ }
+ }
+ /* server not found */
+ return 1;
+}
+
+
+/*
+ * Try to remove all the servers in cache
+ * returns 1 on failure and 0 if all servers could be removed.
+ */
+int
+SMBC_purge_cached_servers(SMBCCTX * context)
+{
+ struct smbc_server_cache * srv;
+ struct smbc_server_cache * next;
+ int could_not_purge_all = 0;
+
+ for (srv = context->internal->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;
+ }
+ }
+ return could_not_purge_all;
+}
diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c
new file mode 100644
index 0000000..465bb3c
--- /dev/null
+++ b/source3/libsmb/libsmb_compat.c
@@ -0,0 +1,581 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client library implementation (Old interface compatibility)
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libsmb_internal.h"
+
+struct smbc_compat_fdlist {
+ SMBCFILE * file;
+ int fd;
+ struct smbc_compat_fdlist *next, *prev;
+};
+
+static SMBCCTX * statcont = NULL;
+static int smbc_compat_initialized = 0;
+static int smbc_compat_nextfd = 0;
+static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL;
+static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL;
+
+/* Find an fd and return the SMBCFILE * or NULL on failure */
+static SMBCFILE *
+find_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+ while (f) {
+ if (f->fd == fd)
+ return f->file;
+ f = f->next;
+ }
+ return NULL;
+}
+
+/* Add an fd, returns 0 on success, -1 on error with errno set */
+static int
+add_fd(SMBCFILE * file)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_avail;
+
+ if (f) {
+ /* We found one that's available */
+ DLIST_REMOVE(smbc_compat_fd_avail, f);
+ } else {
+ /*
+ * None were available, so allocate one. Keep the number of
+ * file descriptors determinate. This allows the application
+ * to allocate bitmaps or mapping of file descriptors based on
+ * a known maximum number of file descriptors that will ever
+ * be returned.
+ */
+ if (smbc_compat_nextfd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ f = SMB_MALLOC_P(struct smbc_compat_fdlist);
+ if (!f) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ f->fd = SMBC_BASE_FD + smbc_compat_nextfd++;
+ }
+
+ f->file = file;
+ DLIST_ADD(smbc_compat_fd_in_use, f);
+
+ return f->fd;
+}
+
+
+
+/* Delete an fd, returns 0 on success */
+static int
+del_fd(int fd)
+{
+ struct smbc_compat_fdlist * f = smbc_compat_fd_in_use;
+
+ while (f) {
+ if (f->fd == fd)
+ break;
+ f = f->next;
+ }
+
+ if (f) {
+ /* found */
+ DLIST_REMOVE(smbc_compat_fd_in_use, f);
+ f->file = NULL;
+ DLIST_ADD(smbc_compat_fd_avail, f);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+int
+smbc_init(smbc_get_auth_data_fn fn,
+ int debug)
+{
+ if (smbc_compat_initialized) {
+ return 0;
+ }
+
+ statcont = smbc_new_context();
+ if (!statcont)
+ return -1;
+
+ smbc_setDebug(statcont, debug);
+ smbc_setFunctionAuthData(statcont, fn);
+
+ if (!smbc_init_context(statcont)) {
+ smbc_free_context(statcont, False);
+ return -1;
+ }
+
+ smbc_compat_initialized = 1;
+
+ return 0;
+}
+
+
+SMBCCTX *
+smbc_set_context(SMBCCTX * context)
+{
+ SMBCCTX *old_context = statcont;
+
+ if (context) {
+ /* Save provided context. It must have been initialized! */
+ statcont = context;
+
+ /* You'd better know what you're doing. We won't help you. */
+ smbc_compat_initialized = 1;
+ }
+
+ return old_context;
+}
+
+
+int
+smbc_open(const char *furl,
+ int flags,
+ mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ smbc_getFunctionClose(statcont)(statcont, file);
+ return fd;
+}
+
+
+int
+smbc_creat(const char *furl,
+ mode_t mode)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionCreat(statcont)(statcont, furl, mode);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1) {
+ /* Hmm... should we delete the file too ? I guess we could try */
+ smbc_getFunctionClose(statcont)(statcont, file);
+ smbc_getFunctionUnlink(statcont)(statcont, furl);
+ }
+ return fd;
+}
+
+
+ssize_t
+smbc_read(int fd,
+ void *buf,
+ size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionRead(statcont)(statcont, file, buf, bufsize);
+}
+
+ssize_t
+smbc_write(int fd,
+ const void *buf,
+ size_t bufsize)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionWrite(statcont)(statcont, file, buf, bufsize);
+}
+
+off_t
+smbc_lseek(int fd,
+ off_t offset,
+ int whence)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionLseek(statcont)(statcont, file, offset, whence);
+}
+
+int
+smbc_close(int fd)
+{
+ SMBCFILE * file = find_fd(fd);
+ del_fd(fd);
+ return smbc_getFunctionClose(statcont)(statcont, file);
+}
+
+int
+smbc_unlink(const char *fname)
+{
+ return smbc_getFunctionUnlink(statcont)(statcont, fname);
+}
+
+int
+smbc_rename(const char *ourl,
+ const char *nurl)
+{
+ return smbc_getFunctionRename(statcont)(statcont, ourl,
+ statcont, nurl);
+}
+
+int
+smbc_opendir(const char *durl)
+{
+ SMBCFILE * file;
+ int fd;
+
+ file = smbc_getFunctionOpendir(statcont)(statcont, durl);
+ if (!file)
+ return -1;
+
+ fd = add_fd(file);
+ if (fd == -1)
+ smbc_getFunctionClosedir(statcont)(statcont, file);
+
+ return fd;
+}
+
+int
+smbc_closedir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ del_fd(dh);
+ return smbc_getFunctionClosedir(statcont)(statcont, file);
+}
+
+int
+smbc_getdents(unsigned int dh,
+ struct smbc_dirent *dirp,
+ int count)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionGetdents(statcont)(statcont, file, dirp, count);
+}
+
+struct smbc_dirent *
+smbc_readdir(unsigned int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionReaddir(statcont)(statcont, file);
+}
+
+const struct libsmb_file_info *smbc_readdirplus(unsigned int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionReaddirPlus(statcont)(statcont, file);
+}
+
+const struct libsmb_file_info *smbc_readdirplus2(unsigned int dh,
+ struct stat *st)
+{
+ SMBCFILE *file = find_fd(dh);
+ return smbc_getFunctionReaddirPlus2(statcont)(statcont, file, st);
+}
+
+off_t
+smbc_telldir(int dh)
+{
+ SMBCFILE * file = find_fd(dh);
+ return smbc_getFunctionTelldir(statcont)(statcont, file);
+}
+
+int
+smbc_lseekdir(int fd,
+ off_t offset)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionLseekdir(statcont)(statcont, file, offset);
+}
+
+int
+smbc_mkdir(const char *durl,
+ mode_t mode)
+{
+ return smbc_getFunctionMkdir(statcont)(statcont, durl, mode);
+}
+
+int
+smbc_rmdir(const char *durl)
+{
+ return smbc_getFunctionRmdir(statcont)(statcont, durl);
+}
+
+int
+smbc_notify(int dh, smbc_bool recursive, uint32_t completion_filter,
+ unsigned callback_timeout_ms,
+ smbc_notify_callback_fn cb, void *private_data)
+{
+ SMBCFILE *dir = find_fd(dh);
+ return smbc_getFunctionNotify(statcont)(
+ statcont, dir, recursive, completion_filter,
+ callback_timeout_ms, cb, private_data);
+}
+
+int
+smbc_stat(const char *url,
+ struct stat *st)
+{
+ return smbc_getFunctionStat(statcont)(statcont, url, st);
+}
+
+int
+smbc_fstat(int fd,
+ struct stat *st)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionFstat(statcont)(statcont, file, st);
+}
+
+int
+smbc_statvfs(char *path,
+ struct statvfs *st)
+{
+ return smbc_getFunctionStatVFS(statcont)(statcont, path, st);
+}
+
+int
+smbc_fstatvfs(int fd,
+ struct statvfs *st)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionFstatVFS(statcont)(statcont, file, st);
+}
+
+int
+smbc_ftruncate(int fd,
+ off_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ return smbc_getFunctionFtruncate(statcont)(statcont, file, size);
+}
+
+int
+smbc_chmod(const char *url,
+ mode_t mode)
+{
+ return smbc_getFunctionChmod(statcont)(statcont, url, mode);
+}
+
+int
+smbc_utimes(const char *fname,
+ struct timeval *tbuf)
+{
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, tbuf);
+}
+
+#ifdef HAVE_UTIME_H
+int
+smbc_utime(const char *fname,
+ struct utimbuf *utbuf)
+{
+ struct timeval tv[2];
+
+ if (utbuf == NULL)
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, NULL);
+
+ tv[0].tv_sec = utbuf->actime;
+ tv[1].tv_sec = utbuf->modtime;
+ tv[0].tv_usec = tv[1].tv_usec = 0;
+
+ return smbc_getFunctionUtimes(statcont)(statcont, fname, tv);
+}
+#endif
+
+int
+smbc_setxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ fname, name,
+ value, size, flags);
+}
+
+int
+smbc_lsetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ fname, name,
+ value, size, flags);
+}
+
+int
+smbc_fsetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionSetxattr(statcont)(statcont,
+ file->fname, name,
+ value, size, flags);
+}
+
+int
+smbc_getxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ fname, name,
+ value, size);
+}
+
+int
+smbc_lgetxattr(const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ fname, name,
+ value, size);
+}
+
+int
+smbc_fgetxattr(int fd,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionGetxattr(statcont)(statcont,
+ file->fname, name,
+ value, size);
+}
+
+int
+smbc_removexattr(const char *fname,
+ const char *name)
+{
+ return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name);
+}
+
+int
+smbc_lremovexattr(const char *fname,
+ const char *name)
+{
+ return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name);
+}
+
+int
+smbc_fremovexattr(int fd,
+ const char *name)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionRemovexattr(statcont)(statcont,
+ file->fname, name);
+}
+
+int
+smbc_listxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ fname, list, size);
+}
+
+int
+smbc_llistxattr(const char *fname,
+ char *list,
+ size_t size)
+{
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ fname, list, size);
+}
+
+int
+smbc_flistxattr(int fd,
+ char *list,
+ size_t size)
+{
+ SMBCFILE * file = find_fd(fd);
+ if (file == NULL) {
+ errno = EBADF;
+ return -1;
+ }
+ return smbc_getFunctionListxattr(statcont)(statcont,
+ file->fname, list, size);
+}
+
+int
+smbc_print_file(const char *fname,
+ const char *printq)
+{
+ return smbc_getFunctionPrintFile(statcont)(statcont, fname,
+ statcont, printq);
+}
+
+int
+smbc_open_print_job(const char *fname)
+{
+ SMBCFILE * file;
+
+ file = smbc_getFunctionOpenPrintJob(statcont)(statcont, fname);
+ if (!file) return -1;
+ return file->cli_fd;
+}
+
+int
+smbc_list_print_jobs(const char *purl,
+ smbc_list_print_job_fn fn)
+{
+ return smbc_getFunctionListPrintJobs(statcont)(statcont, purl, fn);
+}
+
+int
+smbc_unlink_print_job(const char *purl,
+ int id)
+{
+ return smbc_getFunctionUnlinkPrintJob(statcont)(statcont, purl, id);
+}
+
+
diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c
new file mode 100644
index 0000000..441dec1
--- /dev/null
+++ b/source3/libsmb/libsmb_context.c
@@ -0,0 +1,807 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "secrets.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "lib/param/param.h"
+#include "../lib/util/smb_threads.h"
+#include "../lib/util/smb_threads_internal.h"
+
+/*
+ * Is the logging working / configfile read ?
+ */
+static bool SMBC_initialized = false;
+static unsigned int initialized_ctx_count = 0;
+static void *initialized_ctx_count_mutex = NULL;
+
+/*
+ * Do some module- and library-wide initializations
+ */
+static void
+SMBC_module_init(void * punused)
+{
+ bool conf_loaded = False;
+ char *home = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ setup_logging("libsmbclient", DEBUG_STDOUT);
+
+ /* Here we would open the smb.conf file if needed ... */
+
+ home = getenv("HOME");
+ if (home) {
+ char *conf = NULL;
+ if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) {
+ if (lp_load_client(conf)) {
+ conf_loaded = True;
+ } else {
+ DEBUG(5, ("Could not load config file: %s\n",
+ conf));
+ }
+ SAFE_FREE(conf);
+ }
+ }
+
+ if (!conf_loaded) {
+ /*
+ * Well, if that failed, try the get_dyn_CONFIGFILE
+ * Which points to the standard locn, and if that
+ * fails, silently ignore it and use the internal
+ * defaults ...
+ */
+
+ if (!lp_load_client(get_dyn_CONFIGFILE())) {
+ DEBUG(5, ("Could not load config file: %s\n",
+ get_dyn_CONFIGFILE()));
+ } else if (home) {
+ char *conf;
+ /*
+ * We loaded the global config file. Now lets
+ * load user-specific modifications to the
+ * global config.
+ */
+ if (asprintf(&conf,
+ "%s/.smb/smb.conf.append",
+ home) > 0) {
+ if (!lp_load_client_no_reinit(conf)) {
+ DEBUG(10,
+ ("Could not append config file: "
+ "%s\n",
+ conf));
+ }
+ SAFE_FREE(conf);
+ }
+ }
+ }
+
+ load_interfaces(); /* Load the list of interfaces ... */
+
+ reopen_logs(); /* Get logging working ... */
+
+ /*
+ * Block SIGPIPE (from lib/util_sock.c: write())
+ * It is not needed and should not stop execution
+ */
+ BlockSignals(True, SIGPIPE);
+
+ /* Create the mutex we'll use to protect initialized_ctx_count */
+ if (SMB_THREAD_CREATE_MUTEX("initialized_ctx_count_mutex",
+ initialized_ctx_count_mutex) != 0) {
+ smb_panic("SMBC_module_init: "
+ "failed to create 'initialized_ctx_count' mutex");
+ }
+
+ TALLOC_FREE(frame);
+}
+
+
+static void
+SMBC_module_terminate(void)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ secrets_shutdown();
+ gfree_all();
+ SMBC_initialized = false;
+ TALLOC_FREE(frame);
+}
+
+
+/*
+ * Get a new empty handle to fill in with your own info
+ */
+SMBCCTX *
+smbc_new_context(void)
+{
+ SMBCCTX *context;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* The first call to this function should initialize the module */
+ SMB_THREAD_ONCE(&SMBC_initialized, SMBC_module_init, NULL);
+
+ /*
+ * All newly added context fields should be placed in
+ * SMBC_internal_data, not directly in SMBCCTX.
+ */
+ context = SMB_MALLOC_P(SMBCCTX);
+ if (!context) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(context);
+
+ context->internal = SMB_MALLOC_P(struct SMBC_internal_data);
+ if (!context->internal) {
+ TALLOC_FREE(frame);
+ SAFE_FREE(context);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Initialize the context and establish reasonable defaults */
+ ZERO_STRUCTP(context->internal);
+
+ context->internal->lp_ctx = loadparm_init_s3(NULL,
+ loadparm_s3_helpers());
+ if (context->internal->lp_ctx == NULL) {
+ SAFE_FREE(context->internal);
+ SAFE_FREE(context);
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setDebug(context, 0);
+ smbc_setTimeout(context, 20000);
+ smbc_setPort(context, 0);
+
+ smbc_setOptionFullTimeNames(context, False);
+ smbc_setOptionOpenShareMode(context, SMBC_SHAREMODE_DENY_NONE);
+ smbc_setOptionSmbEncryptionLevel(context, SMBC_ENCRYPTLEVEL_DEFAULT);
+ smbc_setOptionUseCCache(context, True);
+ smbc_setOptionCaseSensitive(context, False);
+ smbc_setOptionBrowseMaxLmbCount(context, 3); /* # LMBs to query */
+ smbc_setOptionUrlEncodeReaddirEntries(context, False);
+ smbc_setOptionOneSharePerServer(context, False);
+ smbc_setOptionPosixExtensions(context, false);
+ if (getenv("LIBSMBCLIENT_NO_CCACHE") != NULL) {
+ smbc_setOptionUseCCache(context, false);
+ }
+
+ smbc_setFunctionAuthData(context, SMBC_get_auth_data);
+ smbc_setFunctionCheckServer(context, SMBC_check_server);
+ smbc_setFunctionRemoveUnusedServer(context, SMBC_remove_unused_server);
+
+ smbc_setOptionUserData(context, NULL);
+ smbc_setFunctionAddCachedServer(context, SMBC_add_cached_server);
+ smbc_setFunctionGetCachedServer(context, SMBC_get_cached_server);
+ smbc_setFunctionRemoveCachedServer(context, SMBC_remove_cached_server);
+ smbc_setFunctionPurgeCachedServers(context, SMBC_purge_cached_servers);
+
+ smbc_setFunctionOpen(context, SMBC_open_ctx);
+ smbc_setFunctionCreat(context, SMBC_creat_ctx);
+ smbc_setFunctionRead(context, SMBC_read_ctx);
+ smbc_setFunctionSplice(context, SMBC_splice_ctx);
+ smbc_setFunctionWrite(context, SMBC_write_ctx);
+ smbc_setFunctionClose(context, SMBC_close_ctx);
+ smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
+ smbc_setFunctionRename(context, SMBC_rename_ctx);
+ smbc_setFunctionLseek(context, SMBC_lseek_ctx);
+ smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx);
+ smbc_setFunctionStat(context, SMBC_stat_ctx);
+ smbc_setFunctionStatVFS(context, SMBC_statvfs_ctx);
+ smbc_setFunctionFstatVFS(context, SMBC_fstatvfs_ctx);
+ smbc_setFunctionFstat(context, SMBC_fstat_ctx);
+ smbc_setFunctionOpendir(context, SMBC_opendir_ctx);
+ smbc_setFunctionClosedir(context, SMBC_closedir_ctx);
+ smbc_setFunctionReaddir(context, SMBC_readdir_ctx);
+ smbc_setFunctionReaddirPlus(context, SMBC_readdirplus_ctx);
+ smbc_setFunctionReaddirPlus2(context, SMBC_readdirplus2_ctx);
+ smbc_setFunctionGetdents(context, SMBC_getdents_ctx);
+ smbc_setFunctionMkdir(context, SMBC_mkdir_ctx);
+ smbc_setFunctionRmdir(context, SMBC_rmdir_ctx);
+ smbc_setFunctionTelldir(context, SMBC_telldir_ctx);
+ smbc_setFunctionLseekdir(context, SMBC_lseekdir_ctx);
+ smbc_setFunctionFstatdir(context, SMBC_fstatdir_ctx);
+ smbc_setFunctionNotify(context, SMBC_notify_ctx);
+ smbc_setFunctionChmod(context, SMBC_chmod_ctx);
+ smbc_setFunctionUtimes(context, SMBC_utimes_ctx);
+ smbc_setFunctionSetxattr(context, SMBC_setxattr_ctx);
+ smbc_setFunctionGetxattr(context, SMBC_getxattr_ctx);
+ smbc_setFunctionRemovexattr(context, SMBC_removexattr_ctx);
+ smbc_setFunctionListxattr(context, SMBC_listxattr_ctx);
+
+ smbc_setFunctionOpenPrintJob(context, SMBC_open_print_job_ctx);
+ smbc_setFunctionPrintFile(context, SMBC_print_file_ctx);
+ smbc_setFunctionListPrintJobs(context, SMBC_list_print_jobs_ctx);
+ smbc_setFunctionUnlinkPrintJob(context, SMBC_unlink_print_job_ctx);
+
+ TALLOC_FREE(frame);
+ return context;
+}
+
+/*
+ * 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)
+{
+ TALLOC_CTX *frame;
+ if (!context) {
+ errno = EBADF;
+ return 1;
+ }
+
+ frame = talloc_stackframe();
+
+ if (shutdown_ctx) {
+ SMBCFILE * f;
+ DEBUG(1,("Performing aggressive shutdown.\n"));
+
+ f = context->internal->files;
+ while (f) {
+ SMBCFILE *next = f->next;
+ smbc_getFunctionClose(context)(context, 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 (cli=%p)\n",
+ s, s->cli));
+ 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;
+ TALLOC_FREE(frame);
+ return 1;
+ }
+ if (context->internal->servers) {
+ DEBUG(1, ("Active servers in context, "
+ "free_context failed.\n"));
+ errno = EBUSY;
+ TALLOC_FREE(frame);
+ return 1;
+ }
+ if (context->internal->files) {
+ DEBUG(1, ("Active files in context, "
+ "free_context failed.\n"));
+ errno = EBUSY;
+ TALLOC_FREE(frame);
+ return 1;
+ }
+ }
+
+ /* Things we have to clean up */
+ smbc_setWorkgroup(context, NULL);
+ smbc_setNetbiosName(context, NULL);
+ smbc_setUser(context, NULL);
+
+ DEBUG(3, ("Context %p successfully freed\n", context));
+
+ /* Free any DFS auth context. */
+ TALLOC_FREE(context->internal->creds);
+
+ TALLOC_FREE(context->internal->lp_ctx);
+ 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'");
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+
+/**
+ * Deprecated interface. Do not use. Instead, use the various
+ * smbc_setOption*() functions or smbc_setFunctionAuthDataWithContext().
+ */
+void
+smbc_option_set(SMBCCTX *context,
+ char *option_name,
+ ... /* option_value */)
+{
+ va_list ap;
+ union {
+ int i;
+ bool b;
+ smbc_get_auth_data_with_context_fn auth_fn;
+ void *v;
+ const char *s;
+ } option_value;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ va_start(ap, option_name);
+
+ if (strcmp(option_name, "debug_to_stderr") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionDebugToStderr(context, option_value.b);
+
+ } else if (strcmp(option_name, "full_time_names") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionFullTimeNames(context, option_value.b);
+
+ } else if (strcmp(option_name, "open_share_mode") == 0) {
+ option_value.i = va_arg(ap, int);
+ smbc_setOptionOpenShareMode(context, option_value.i);
+
+ } else if (strcmp(option_name, "auth_function") == 0) {
+ option_value.auth_fn =
+ va_arg(ap, smbc_get_auth_data_with_context_fn);
+ smbc_setFunctionAuthDataWithContext(context, option_value.auth_fn);
+
+ } else if (strcmp(option_name, "user_data") == 0) {
+ option_value.v = va_arg(ap, void *);
+ smbc_setOptionUserData(context, option_value.v);
+
+ } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
+ option_value.s = va_arg(ap, const char *);
+ if (strcmp(option_value.s, "none") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_NONE);
+ } else if (strcmp(option_value.s, "request") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_REQUEST);
+ } else if (strcmp(option_value.s, "require") == 0) {
+ smbc_setOptionSmbEncryptionLevel(context,
+ SMBC_ENCRYPTLEVEL_REQUIRE);
+ }
+
+ } else if (strcmp(option_name, "browse_max_lmb_count") == 0) {
+ option_value.i = va_arg(ap, int);
+ smbc_setOptionBrowseMaxLmbCount(context, option_value.i);
+
+ } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionUrlEncodeReaddirEntries(context, option_value.b);
+
+ } else if (strcmp(option_name, "one_share_per_server") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionOneSharePerServer(context, option_value.b);
+
+ } else if (strcmp(option_name, "use_kerberos") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionUseKerberos(context, option_value.b);
+
+ } else if (strcmp(option_name, "fallback_after_kerberos") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionFallbackAfterKerberos(context, option_value.b);
+
+ } else if (strcmp(option_name, "use_ccache") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionUseCCache(context, option_value.b);
+
+ } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) {
+ option_value.b = (bool) va_arg(ap, int);
+ smbc_setOptionNoAutoAnonymousLogin(context, option_value.b);
+ }
+
+ va_end(ap);
+ TALLOC_FREE(frame);
+}
+
+
+/*
+ * Deprecated interface. Do not use. Instead, use the various
+ * smbc_getOption*() functions.
+ */
+void *
+smbc_option_get(SMBCCTX *context,
+ char *option_name)
+{
+ if (strcmp(option_name, "debug_stderr") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionDebugToStderr(context);
+#else
+ return (void *) smbc_getOptionDebugToStderr(context);
+#endif
+
+ } else if (strcmp(option_name, "full_time_names") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionFullTimeNames(context);
+#else
+ return (void *) smbc_getOptionFullTimeNames(context);
+#endif
+
+ } else if (strcmp(option_name, "open_share_mode") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionOpenShareMode(context);
+#else
+ return (void *) smbc_getOptionOpenShareMode(context);
+#endif
+
+ } else if (strcmp(option_name, "auth_function") == 0) {
+ return (void *) smbc_getFunctionAuthDataWithContext(context);
+
+ } else if (strcmp(option_name, "user_data") == 0) {
+ return smbc_getOptionUserData(context);
+
+ } else if (strcmp(option_name, "smb_encrypt_level") == 0) {
+ switch(smbc_getOptionSmbEncryptionLevel(context))
+ {
+ case SMBC_ENCRYPTLEVEL_DEFAULT:
+ return discard_const_p(void, "default");
+ case 0:
+ return discard_const_p(void, "none");
+ case 1:
+ return discard_const_p(void, "request");
+ case 2:
+ return discard_const_p(void, "require");
+ }
+
+ } else if (strcmp(option_name, "smb_encrypt_on") == 0) {
+ SMBCSRV *s;
+ unsigned int num_servers = 0;
+
+ for (s = context->internal->servers; s; s = s->next) {
+ num_servers++;
+ if (!cli_state_is_encryption_on(s->cli)) {
+ return (void *)false;
+ }
+ }
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) (bool) (num_servers > 0);
+#else
+ return (void *) (bool) (num_servers > 0);
+#endif
+
+ } else if (strcmp(option_name, "browse_max_lmb_count") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionBrowseMaxLmbCount(context);
+#else
+ return (void *) smbc_getOptionBrowseMaxLmbCount(context);
+#endif
+
+ } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *)(intptr_t) smbc_getOptionUrlEncodeReaddirEntries(context);
+#else
+ return (void *) (bool) smbc_getOptionUrlEncodeReaddirEntries(context);
+#endif
+
+ } else if (strcmp(option_name, "one_share_per_server") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionOneSharePerServer(context);
+#else
+ return (void *) (bool) smbc_getOptionOneSharePerServer(context);
+#endif
+
+ } else if (strcmp(option_name, "use_kerberos") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionUseKerberos(context);
+#else
+ return (void *) (bool) smbc_getOptionUseKerberos(context);
+#endif
+
+ } else if (strcmp(option_name, "fallback_after_kerberos") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *)(intptr_t) smbc_getOptionFallbackAfterKerberos(context);
+#else
+ return (void *) (bool) smbc_getOptionFallbackAfterKerberos(context);
+#endif
+
+ } else if (strcmp(option_name, "use_ccache") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionUseCCache(context);
+#else
+ return (void *) (bool) smbc_getOptionUseCCache(context);
+#endif
+
+ } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) {
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+ return (void *) (intptr_t) smbc_getOptionNoAutoAnonymousLogin(context);
+#else
+ return (void *) (bool) smbc_getOptionNoAutoAnonymousLogin(context);
+#endif
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Initialize the library, etc.
+ *
+ * We accept a struct containing handle information.
+ * valid values for info->debug from 0 to 100,
+ * and insist that info->fn must be non-null.
+ */
+SMBCCTX *
+smbc_init_context(SMBCCTX *context)
+{
+ int pid;
+ TALLOC_CTX *frame;
+
+ if (!context) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ /* Do not initialise the same client twice */
+ if (context->internal->initialized) {
+ return NULL;
+ }
+
+ frame = talloc_stackframe();
+
+ if ((!smbc_getFunctionAuthData(context) &&
+ !smbc_getFunctionAuthDataWithContext(context)) ||
+ smbc_getDebug(context) < 0 ||
+ smbc_getDebug(context) > 100) {
+
+ TALLOC_FREE(frame);
+ errno = EINVAL;
+ return NULL;
+
+ }
+
+ if (!smbc_getUser(context)) {
+ /*
+ * FIXME: Is this the best way to get the user info?
+ */
+ char *user = getenv("USER");
+ /* walk around as "guest" if no username can be found */
+ if (!user) {
+ user = SMB_STRDUP("guest");
+ } else {
+ user = SMB_STRDUP(user);
+ }
+
+ if (!user) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setUser(context, user);
+ SAFE_FREE(user);
+
+ if (!smbc_getUser(context)) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ if (!smbc_getNetbiosName(context)) {
+ /*
+ * We try to get our netbios name from the config. If that
+ * fails we fall back on constructing our netbios name from
+ * our hostname etc
+ */
+ char *netbios_name;
+ if (lp_netbios_name()) {
+ netbios_name = SMB_STRDUP(lp_netbios_name());
+ } else {
+ /*
+ * Hmmm, I want to get hostname as well, but I am too
+ * lazy for the moment
+ */
+ pid = getpid();
+ netbios_name = (char *)SMB_MALLOC(17);
+ if (!netbios_name) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ slprintf(netbios_name, 16,
+ "smbc%s%d", smbc_getUser(context), pid);
+ }
+
+ if (!netbios_name) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ smbc_setNetbiosName(context, netbios_name);
+ SAFE_FREE(netbios_name);
+
+ if (!smbc_getNetbiosName(context)) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ DEBUG(1, ("Using netbios name %s.\n", smbc_getNetbiosName(context)));
+
+ if (!smbc_getWorkgroup(context)) {
+ const char *workgroup;
+
+ if (lp_workgroup()) {
+ workgroup = lp_workgroup();
+ } else {
+ /* TODO: Think about a decent default workgroup */
+ workgroup = "samba";
+ }
+
+ smbc_setWorkgroup(context, workgroup);
+
+ if (!smbc_getWorkgroup(context)) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ DEBUG(1, ("Using workgroup %s.\n", smbc_getWorkgroup(context)));
+
+ /* shortest timeout is 1 second */
+ if (smbc_getTimeout(context) > 0 && smbc_getTimeout(context) < 1000)
+ smbc_setTimeout(context, 1000);
+
+ context->internal->initialized = True;
+
+ /* 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'");
+ }
+
+ initialized_ctx_count++;
+
+ /* Unlock the mutex */
+ if (SMB_THREAD_UNLOCK(initialized_ctx_count_mutex) != 0) {
+ smb_panic("error unlocking 'initialized_ctx_count'");
+ }
+
+ TALLOC_FREE(frame);
+ return context;
+}
+
+
+/* Return the version of samba, and thus libsmbclient */
+const char *
+smbc_version(void)
+{
+ return samba_version_string();
+}
+
+/*
+ * Set the credentials so DFS will work when following referrals.
+ * This function is broken and must be removed. No SMBCCTX arg...
+ * JRA.
+ */
+
+void
+smbc_set_credentials(const char *workgroup,
+ const char *user,
+ const char *password,
+ smbc_bool use_kerberos,
+ const char *signing_state)
+{
+ d_printf("smbc_set_credentials is obsolete. Replace with smbc_set_credentials_with_fallback().\n");
+}
+
+void smbc_set_credentials_with_fallback(SMBCCTX *context,
+ const char *workgroup,
+ const char *user,
+ const char *password)
+{
+ struct cli_credentials *creds = NULL;
+ enum credentials_use_kerberos kerberos_state =
+ CRED_USE_KERBEROS_DISABLED;
+
+ if (! context) {
+
+ return;
+ }
+
+ if (! workgroup || ! *workgroup) {
+ workgroup = smbc_getWorkgroup(context);
+ }
+
+ if (! user) {
+ user = smbc_getUser(context);
+ }
+
+ if (! password) {
+ password = "";
+ }
+
+ creds = cli_credentials_init(NULL);
+ if (creds == NULL) {
+ DEBUG(0, ("smbc_set_credentials_with_fallback: allocation fail\n"));
+ return;
+ }
+
+ cli_credentials_set_conf(creds, context->internal->lp_ctx);
+
+ if (smbc_getOptionUseKerberos(context)) {
+ kerberos_state = CRED_USE_KERBEROS_REQUIRED;
+
+ if (smbc_getOptionFallbackAfterKerberos(context)) {
+ kerberos_state = CRED_USE_KERBEROS_DESIRED;
+ }
+ }
+
+ cli_credentials_set_username(creds, user, CRED_SPECIFIED);
+ cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+ cli_credentials_set_domain(creds, workgroup, CRED_SPECIFIED);
+ cli_credentials_set_kerberos_state(creds,
+ kerberos_state,
+ CRED_SPECIFIED);
+ if (smbc_getOptionUseCCache(context)) {
+ uint32_t gensec_features;
+
+ gensec_features = cli_credentials_get_gensec_features(creds);
+ gensec_features |= GENSEC_FEATURE_NTLM_CCACHE;
+ cli_credentials_set_gensec_features(creds,
+ gensec_features,
+ CRED_SPECIFIED);
+ }
+
+ TALLOC_FREE(context->internal->creds);
+ context->internal->creds = creds;
+}
diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c
new file mode 100644
index 0000000..34d6731
--- /dev/null
+++ b/source3/libsmb/libsmb_dir.c
@@ -0,0 +1,2726 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/namequery.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "libsmb/nmblib.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "../libcli/security/security.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/time_basic.h"
+#include "lib/util/string_wrappers.h"
+
+/*
+ * Routine to open a directory
+ * We accept the URL syntax explained in SMBC_parse_path(), above.
+ */
+
+static void remove_dirplus(SMBCFILE *dir)
+{
+ struct smbc_dirplus_list *d = NULL;
+
+ d = dir->dirplus_list;
+ while (d != NULL) {
+ struct smbc_dirplus_list *f = d;
+ d = d->next;
+
+ SAFE_FREE(f->smb_finfo->short_name);
+ SAFE_FREE(f->smb_finfo->name);
+ SAFE_FREE(f->smb_finfo);
+ SAFE_FREE(f);
+ }
+
+ dir->dirplus_list = NULL;
+ dir->dirplus_end = NULL;
+ dir->dirplus_next = NULL;
+}
+
+static void
+remove_dir(SMBCFILE *dir)
+{
+ struct smbc_dir_list *d,*f;
+
+ d = dir->dir_list;
+ while (d) {
+
+ f = d; d = d->next;
+
+ SAFE_FREE(f->dirent);
+ SAFE_FREE(f);
+
+ }
+
+ dir->dir_list = dir->dir_end = dir->dir_next = NULL;
+
+}
+
+static int
+add_dirent(SMBCFILE *dir,
+ const char *name,
+ const char *comment,
+ uint32_t type)
+{
+ struct smbc_dirent *dirent;
+ int size;
+ int name_length = (name == NULL ? 0 : strlen(name));
+ int comment_len = (comment == NULL ? 0 : strlen(comment));
+
+ /*
+ * Allocate space for the dirent, which must be increased by the
+ * size of the name and the comment and 1 each for the null terminator.
+ */
+
+ size = sizeof(struct smbc_dirent) + name_length + comment_len + 2;
+
+ dirent = (struct smbc_dirent *)SMB_MALLOC(size);
+
+ if (!dirent) {
+
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+
+ ZERO_STRUCTP(dirent);
+
+ if (dir->dir_list == NULL) {
+
+ dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list);
+ if (!dir->dir_list) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_list);
+
+ dir->dir_end = dir->dir_next = dir->dir_list;
+ }
+ else {
+
+ dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list);
+
+ if (!dir->dir_end->next) {
+
+ SAFE_FREE(dirent);
+ dir->dir_error = ENOMEM;
+ return -1;
+
+ }
+ ZERO_STRUCTP(dir->dir_end->next);
+
+ dir->dir_end = dir->dir_end->next;
+ }
+
+ dir->dir_end->next = NULL;
+ dir->dir_end->dirent = dirent;
+
+ dirent->smbc_type = type;
+ dirent->namelen = name_length;
+ dirent->commentlen = comment_len;
+ dirent->dirlen = size;
+
+ /*
+ * dirent->namelen + 1 includes the null (no null termination needed)
+ * Ditto for dirent->commentlen.
+ * The space for the two null bytes was allocated.
+ */
+ strncpy(dirent->name, (name?name:""), dirent->namelen + 1);
+ dirent->comment = (char *)(&dirent->name + dirent->namelen + 1);
+ strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1);
+
+ return 0;
+
+}
+
+static int add_dirplus(SMBCFILE *dir, struct file_info *finfo)
+{
+ struct smbc_dirplus_list *new_entry = NULL;
+ struct libsmb_file_info *info = NULL;
+
+ new_entry = SMB_MALLOC_P(struct smbc_dirplus_list);
+ if (new_entry == NULL) {
+ dir->dir_error = ENOMEM;
+ return -1;
+ }
+ ZERO_STRUCTP(new_entry);
+ new_entry->ino = finfo->ino;
+
+ info = SMB_MALLOC_P(struct libsmb_file_info);
+ if (info == NULL) {
+ SAFE_FREE(new_entry);
+ dir->dir_error = ENOMEM;
+ return -1;
+ }
+
+ ZERO_STRUCTP(info);
+
+ info->btime_ts = finfo->btime_ts;
+ info->atime_ts = finfo->atime_ts;
+ info->ctime_ts = finfo->ctime_ts;
+ info->mtime_ts = finfo->mtime_ts;
+ info->gid = finfo->gid;
+ info->attrs = finfo->attr;
+ info->size = finfo->size;
+ info->uid = finfo->uid;
+ info->name = SMB_STRDUP(finfo->name);
+ if (info->name == NULL) {
+ SAFE_FREE(info);
+ SAFE_FREE(new_entry);
+ dir->dir_error = ENOMEM;
+ return -1;
+ }
+
+ if (finfo->short_name) {
+ info->short_name = SMB_STRDUP(finfo->short_name);
+ } else {
+ info->short_name = SMB_STRDUP("");
+ }
+
+ if (info->short_name == NULL) {
+ SAFE_FREE(info->name);
+ SAFE_FREE(info);
+ SAFE_FREE(new_entry);
+ dir->dir_error = ENOMEM;
+ return -1;
+ }
+ new_entry->smb_finfo = info;
+
+ /* Now add to the list. */
+ if (dir->dirplus_list == NULL) {
+ /* Empty list - point everything at new_entry. */
+ dir->dirplus_list = new_entry;
+ dir->dirplus_end = new_entry;
+ dir->dirplus_next = new_entry;
+ } else {
+ /* Append to list but leave the ->next cursor alone. */
+ dir->dirplus_end->next = new_entry;
+ dir->dirplus_end = new_entry;
+ }
+
+ return 0;
+}
+
+static void
+list_unique_wg_fn(const char *name,
+ uint32_t type,
+ const char *comment,
+ void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ struct smbc_dir_list *dir_list;
+ struct smbc_dirent *dirent;
+ int dirent_type;
+ int do_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 */
+ /* Change cli_NetServerEnum to take a fn
+ returning NTSTATUS... JRA. */
+ }
+
+ /* 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 (! do_remove &&
+ strcmp(dir_list->dirent->name, dirent->name) == 0) {
+ /* Duplicate. End end of list need to be removed. */
+ do_remove = 1;
+ }
+
+ if (do_remove && dir_list->next == dir->dir_end) {
+ /* Found the end of the list. Remove it. */
+ dir->dir_end = dir_list;
+ free(dir_list->next);
+ free(dirent);
+ dir_list->next = NULL;
+ break;
+ }
+ }
+}
+
+static void
+list_fn(const char *name,
+ uint32_t type,
+ const char *comment,
+ void *state)
+{
+ SMBCFILE *dir = (SMBCFILE *)state;
+ int dirent_type;
+
+ /*
+ * We need to process the type a little ...
+ *
+ * Disk share = 0x00000000
+ * Print share = 0x00000001
+ * Comms share = 0x00000002 (obsolete?)
+ * IPC$ share = 0x00000003
+ *
+ * administrative shares:
+ * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000
+ */
+
+ if (dir->dir_type == SMBC_FILE_SHARE) {
+ switch (type) {
+ case 0 | 0x80000000:
+ case 0:
+ dirent_type = SMBC_FILE_SHARE;
+ break;
+
+ case 1:
+ dirent_type = SMBC_PRINTER_SHARE;
+ break;
+
+ case 2:
+ dirent_type = SMBC_COMMS_SHARE;
+ break;
+
+ case 3 | 0x80000000:
+ case 3:
+ dirent_type = SMBC_IPC_SHARE;
+ break;
+
+ default:
+ dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */
+ break;
+ }
+ }
+ else {
+ 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 */
+ /* Change cli_NetServerEnum to take a fn
+ returning NTSTATUS... JRA. */
+ }
+}
+
+static NTSTATUS
+dir_list_fn(struct file_info *finfo,
+ const char *mask,
+ void *state)
+{
+ SMBCFILE *dirp = (SMBCFILE *)state;
+ int ret;
+
+ if (add_dirent((SMBCFILE *)state, finfo->name, "",
+ (finfo->attr&FILE_ATTRIBUTE_DIRECTORY?SMBC_DIR:SMBC_FILE)) < 0) {
+ SMBCFILE *dir = (SMBCFILE *)state;
+ return map_nt_error_from_unix(dir->dir_error);
+ }
+ ret = add_dirplus(dirp, finfo);
+ if (ret < 0) {
+ return map_nt_error_from_unix(dirp->dir_error);
+ }
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS
+net_share_enum_rpc(struct cli_state *cli,
+ void (*fn)(const char *name,
+ uint32_t type,
+ const char *comment,
+ void *state),
+ void *state)
+{
+ uint32_t i;
+ WERROR result;
+ uint32_t preferred_len = 0xffffffff;
+ uint32_t type;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 ctr1;
+ fstring name = "";
+ fstring comment = "";
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS nt_status;
+ uint32_t resume_handle = 0;
+ uint32_t total_entries = 0;
+ struct dcerpc_binding_handle *b;
+
+ /* Open the server service pipe */
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("net_share_enum_rpc pipe open fail!\n"));
+ goto done;
+ }
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(ctr1);
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ b = pipe_hnd->binding_handle;
+
+ /* Issue the NetShareEnum RPC call and retrieve the response */
+ nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(),
+ pipe_hnd->desthost,
+ &info_ctr,
+ preferred_len,
+ &total_entries,
+ &resume_handle,
+ &result);
+
+ /* Was it successful? */
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ /* Nope. Go clean up. */
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* Nope. Go clean up. */
+ nt_status = werror_to_ntstatus(result);
+ goto done;
+ }
+
+ if (total_entries == 0) {
+ /* Nope. Go clean up. */
+ nt_status = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ /* For each returned entry... */
+ for (i = 0; i < info_ctr.ctr.ctr1->count; i++) {
+
+ /* pull out the share name */
+ fstrcpy(name, info_ctr.ctr.ctr1->array[i].name);
+
+ /* pull out the share's comment */
+ fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment);
+
+ /* Get the type value */
+ type = info_ctr.ctr.ctr1->array[i].type;
+
+ /* Add this share to the list */
+ (*fn)(name, type, comment, state);
+ }
+
+done:
+ /* Close the server service pipe */
+ TALLOC_FREE(pipe_hnd);
+
+ /* Tell 'em if it worked */
+ return nt_status;
+}
+
+
+/*
+ * Verify that the options specified in a URL are valid
+ */
+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;
+
+ /* Currently, we don't support any options. */
+ return -1;
+}
+
+
+SMBCFILE *
+SMBC_opendir_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *options = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ size_t path_len = 0;
+ uint16_t port = 0;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *dir = NULL;
+ struct sockaddr_storage rem_ss;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ DEBUG(4, ("no valid context\n"));
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8192;
+ return NULL;
+
+ }
+
+ if (!fname) {
+ DEBUG(4, ("no valid fname\n"));
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8193;
+ return NULL;
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ &options)) {
+ DEBUG(4, ("no valid path\n"));
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8194;
+ return NULL;
+ }
+
+ 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));
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8195;
+ return NULL;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ dir = SMB_MALLOC_P(SMBCFILE);
+
+ if (!dir) {
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(dir);
+
+ dir->cli_fd = 0;
+ dir->fname = SMB_STRDUP(fname);
+ if (dir->fname == NULL) {
+ SAFE_FREE(dir);
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ dir->srv = NULL;
+ dir->offset = 0;
+ dir->file = False;
+ dir->dir_list = dir->dir_next = dir->dir_end = NULL;
+
+ if (server[0] == (char)0) {
+
+ size_t i;
+ size_t count = 0;
+ size_t max_lmb_count;
+ struct sockaddr_storage *ip_list;
+ struct sockaddr_storage server_addr;
+ struct cli_credentials *creds = NULL;
+ NTSTATUS status;
+
+ if (share[0] != (char)0 || path[0] != (char)0) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8196;
+ return NULL;
+ }
+
+ /* Determine how many local master browsers to query */
+ max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0
+ ? INT_MAX
+ : smbc_getOptionBrowseMaxLmbCount(context));
+
+ creds = cli_credentials_init(frame);
+ if (creds == NULL) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ (void)cli_credentials_set_username(creds, user, CRED_SPECIFIED);
+ (void)cli_credentials_set_password(creds, password, CRED_SPECIFIED);
+
+ /*
+ * 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.
+ */
+
+ ip_list = NULL;
+ status = name_resolve_bcast(talloc_tos(),
+ MSBROWSE,
+ 1,
+ &ip_list,
+ &count);
+ if (!NT_STATUS_IS_OK(status))
+ {
+
+ TALLOC_FREE(ip_list);
+
+ if (!find_master_ip(workgroup, &server_addr)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ ip_list = (struct sockaddr_storage *)talloc_memdup(
+ talloc_tos(), &server_addr,
+ sizeof(server_addr));
+ if (ip_list == NULL) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+ count = 1;
+ }
+
+ for (i = 0; i < count && i < max_lmb_count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ char *wg_ptr = NULL;
+ struct cli_state *cli = NULL;
+
+ print_sockaddr(addr, sizeof(addr), &ip_list[i]);
+ DEBUG(99, ("Found master browser %zu of %zu: %s\n",
+ i+1, MAX(count, max_lmb_count),
+ addr));
+
+ cli = get_ipc_connect_master_ip(talloc_tos(),
+ &ip_list[i],
+ creds,
+ &wg_ptr);
+ /* cli == NULL is the master browser refused to talk or
+ could not be found */
+ if (!cli) {
+ continue;
+ }
+
+ workgroup = talloc_strdup(frame, wg_ptr);
+ server = talloc_strdup(frame, smbXcli_conn_remote_name(cli->conn));
+
+ cli_shutdown(cli);
+
+ if (!workgroup || !server) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ 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(frame, context, True, server, port, "IPC$",
+ &workgroup, &user, &password);
+ if (!srv) {
+ continue;
+ }
+
+ if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
+ continue;
+ }
+
+ 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)) {
+ continue;
+ }
+ }
+
+ TALLOC_FREE(ip_list);
+ } else {
+ /*
+ * Server not an empty string ... Check the rest and see what
+ * gives
+ */
+ if (*share == '\0') {
+ if (*path != '\0') {
+
+ /* Should not have empty share with path */
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8197;
+ return NULL;
+
+ }
+
+ /*
+ * We don't know if <server> is really a server name
+ * or is a workgroup/domain name. If we already have
+ * a server structure for it, we'll use it.
+ * Otherwise, check to see if <server><1D>,
+ * <server><1B>, or <server><20> translates. We check
+ * to see if <server> is an IP address first.
+ */
+
+ /*
+ * See if we have an existing server. Do not
+ * establish a connection if one does not already
+ * exist.
+ */
+ srv = SMBC_server(frame, context, False,
+ server, port, "IPC$",
+ &workgroup, &user, &password);
+
+ /*
+ * If no existing server and not an IP addr, look for
+ * LMB or DMB
+ */
+ if (!srv &&
+ !is_ipaddress(server) &&
+ (resolve_name(server, &rem_ss, 0x1d, false) || /* LMB */
+ resolve_name(server, &rem_ss, 0x1b, false) )) { /* DMB */
+ /*
+ * "server" is actually a workgroup name,
+ * not a server. Make this clear.
+ */
+ char *wgroup = server;
+ fstring buserver;
+
+ dir->dir_type = SMBC_SERVER;
+
+ /*
+ * Get the backup list ...
+ */
+ if (!name_status_find(wgroup, 0, 0,
+ &rem_ss, buserver)) {
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &rem_ss);
+ DEBUG(0,("Could not get name of "
+ "local/domain master browser "
+ "for workgroup %s from "
+ "address %s\n",
+ wgroup,
+ addr));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = EPERM;
+ return NULL;
+
+ }
+
+ /*
+ * Get a connection to IPC$ on the server if
+ * we do not already have one
+ */
+ srv = SMBC_server(frame, context, True,
+ buserver, port, "IPC$",
+ &workgroup,
+ &user, &password);
+ if (!srv) {
+ DEBUG(0, ("got no contact to IPC$\n"));
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dir->srv = srv;
+
+ if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /* Now, list the servers ... */
+ if (!cli_NetServerEnum(srv->cli, wgroup,
+ 0x0000FFFE, list_fn,
+ (void *)dir)) {
+
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ } else if (srv ||
+ (resolve_name(server, &rem_ss, 0x20, false))) {
+ NTSTATUS status;
+
+ /*
+ * If we hadn't found the server, get one now
+ */
+ if (!srv) {
+ srv = SMBC_server(frame, context, True,
+ server, port, "IPC$",
+ &workgroup,
+ &user, &password);
+ }
+
+ if (!srv) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dir->dir_type = SMBC_FILE_SHARE;
+ dir->srv = srv;
+
+ /* List the shares ... */
+
+ status = net_share_enum_rpc(srv->cli,
+ list_fn,
+ (void *)dir);
+ if (!NT_STATUS_IS_OK(status) &&
+ smbXcli_conn_protocol(srv->cli->conn) <=
+ PROTOCOL_NT1) {
+ /*
+ * Only call cli_RNetShareEnum()
+ * on SMB1 connections, not SMB2+.
+ */
+ int rc = cli_RNetShareEnum(srv->cli,
+ list_fn,
+ (void *)dir);
+ if (rc != 0) {
+ status = cli_nt_error(srv->cli);
+ } else {
+ status = NT_STATUS_OK;
+ }
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Set cli->raw_status so SMBC_errno()
+ * will correctly return the error.
+ */
+ srv->cli->raw_status = status;
+ if (dir != NULL) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ errno = map_errno_from_nt_status(
+ status);
+ return NULL;
+ }
+ } else {
+ /* Neither the workgroup nor server exists */
+ errno = ECONNREFUSED;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ }
+ else {
+ /*
+ * The server and share are specified ... work from
+ * there ...
+ */
+ char *targetpath;
+ struct cli_state *targetcli;
+ struct cli_credentials *creds = NULL;
+ NTSTATUS status;
+
+ /* We connect to the server and list the directory */
+ dir->dir_type = SMBC_FILE_SHARE;
+
+ srv = SMBC_server(frame, context, True, server, port, share,
+ &workgroup, &user, &password);
+
+ if (!srv) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dir->srv = srv;
+
+ /* Now, list the files ... */
+
+ path_len = strlen(path);
+ path = talloc_asprintf_append(path, "\\*");
+ if (!path) {
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ creds = context->internal->creds;
+
+ status = cli_resolve_path(
+ frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ status = cli_list(targetcli, targetpath,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+ dir_list_fn, (void *)dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ int saved_errno;
+ if (dir) {
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir);
+ }
+ saved_errno = cli_status_to_errno(status);
+
+ if (saved_errno == EINVAL) {
+ struct stat sb = {0};
+ /*
+ * See if they asked to opendir
+ * something other than a directory.
+ * If so, the converted error value we
+ * got would have been EINVAL rather
+ * than ENOTDIR.
+ */
+ path[path_len] = '\0'; /* restore original path */
+
+ status = SMBC_getatr(
+ context,
+ srv,
+ path,
+ &sb);
+ if (NT_STATUS_IS_OK(status) &&
+ !S_ISDIR(sb.st_mode)) {
+
+ /* It is. Correct the error value */
+ saved_errno = ENOTDIR;
+ }
+ }
+
+ /*
+ * If there was an error and the server is no
+ * good any more...
+ */
+ if (cli_is_error(targetcli) &&
+ smbc_getFunctionCheckServer(context)(context, srv)) {
+
+ /* ... then remove it. */
+ 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);
+ }
+ }
+
+ TALLOC_FREE(frame);
+ errno = saved_errno;
+ return NULL;
+ }
+ }
+
+ }
+
+ DLIST_ADD(context->internal->files, dir);
+ TALLOC_FREE(frame);
+ return dir;
+
+}
+
+/*
+ * Routine to close a directory
+ */
+
+int
+SMBC_closedir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ TALLOC_CTX *frame = NULL;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (dir == NULL) {
+ return 0;
+ }
+
+ frame = talloc_stackframe();
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ remove_dir(dir); /* Clean it up */
+ remove_dirplus(dir);
+
+ DLIST_REMOVE(context->internal->files, dir);
+
+ SAFE_FREE(dir->fname);
+ SAFE_FREE(dir); /* Free the space too */
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+static int
+smbc_readdir_internal(SMBCCTX * context,
+ struct smbc_dirent *dest,
+ struct smbc_dirent *src,
+ int max_namebuf_len)
+{
+ if (smbc_getOptionUrlEncodeReaddirEntries(context)) {
+ int remaining_len;
+
+ /* url-encode the name. get back remaining buffer space */
+ remaining_len =
+ smbc_urlencode(dest->name, src->name, max_namebuf_len);
+
+ /* -1 means no null termination. */
+ if (remaining_len < 0) {
+ return -1;
+ }
+
+ /* We now know the name length */
+ dest->namelen = strlen(dest->name);
+
+ if (dest->namelen + 1 < 1) {
+ /* Integer wrap. */
+ return -1;
+ }
+
+ if (dest->namelen + 1 >= max_namebuf_len) {
+ /* Out of space for comment. */
+ return -1;
+ }
+
+ /* Save the pointer to the beginning of the comment */
+ dest->comment = dest->name + dest->namelen + 1;
+
+ if (remaining_len < 1) {
+ /* No room for comment null termination. */
+ return -1;
+ }
+
+ /* Copy the comment */
+ strlcpy(dest->comment, src->comment, remaining_len);
+
+ /* Save other fields */
+ dest->smbc_type = src->smbc_type;
+ dest->commentlen = strlen(dest->comment);
+ dest->dirlen = ((dest->comment + dest->commentlen + 1) -
+ (char *) dest);
+ } else {
+
+ /* No encoding. Just copy the entry as is. */
+ if (src->dirlen > max_namebuf_len) {
+ return -1;
+ }
+ memcpy(dest, src, src->dirlen);
+ if (src->namelen + 1 < 1) {
+ /* Integer wrap */
+ return -1;
+ }
+ if (src->namelen + 1 >= max_namebuf_len) {
+ /* Comment off the end. */
+ return -1;
+ }
+ dest->comment = (char *)(&dest->name + src->namelen + 1);
+ }
+ return 0;
+}
+
+/*
+ * Routine to get a directory entry
+ */
+
+struct smbc_dirent *
+SMBC_readdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ int maxlen;
+ int ret;
+ struct smbc_dirent *dirp, *dirent;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n"));
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ if (!dir->dir_next) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dirent = dir->dir_next->dirent;
+ if (!dirent) {
+
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return NULL;
+
+ }
+
+ dirp = &context->internal->dirent;
+ maxlen = sizeof(context->internal->_dirent_name);
+
+ ret = smbc_readdir_internal(context, dirp, dirent, maxlen);
+ if (ret == -1) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ dir->dir_next = dir->dir_next->next;
+
+ /*
+ * If we are returning file entries, we
+ * have a duplicate list in dirplus.
+ *
+ * Update dirplus_next also so readdir and
+ * readdirplus are kept in sync.
+ */
+ if (dir->dirplus_list != NULL) {
+ dir->dirplus_next = dir->dirplus_next->next;
+ }
+
+ TALLOC_FREE(frame);
+ return dirp;
+}
+
+/*
+ * Routine to get a directory entry with all attributes
+ */
+
+const struct libsmb_file_info *
+SMBC_readdirplus_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ struct libsmb_file_info *smb_finfo = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+
+ if (context == NULL || !context->internal->initialized) {
+ DBG_ERR("Invalid context in SMBC_readdirplus_ctx()\n");
+ TALLOC_FREE(frame);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+ DBG_ERR("Invalid dir in SMBC_readdirplus_ctx()\n");
+ TALLOC_FREE(frame);
+ errno = EBADF;
+ return NULL;
+ }
+
+ if (dir->dirplus_next == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ smb_finfo = dir->dirplus_next->smb_finfo;
+ if (smb_finfo == NULL) {
+ TALLOC_FREE(frame);
+ errno = ENOENT;
+ return NULL;
+ }
+ dir->dirplus_next = dir->dirplus_next->next;
+
+ /*
+ * If we are returning file entries, we
+ * have a duplicate list in dir_list
+ *
+ * Update dir_next also so readdir and
+ * readdirplus are kept in sync.
+ */
+ if (dir->dir_list) {
+ dir->dir_next = dir->dir_next->next;
+ }
+
+ TALLOC_FREE(frame);
+ return smb_finfo;
+}
+
+/*
+ * Routine to get a directory entry plus a filled in stat structure if
+ * requested.
+ */
+
+const struct libsmb_file_info *SMBC_readdirplus2_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ struct stat *st)
+{
+ struct libsmb_file_info *smb_finfo = NULL;
+ struct smbc_dirplus_list *dp_list = NULL;
+ ino_t ino;
+ char *full_pathname = NULL;
+ char *workgroup = NULL;
+ char *server = NULL;
+ uint16_t port = 0;
+ char *share = NULL;
+ char *path = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *options = NULL;
+ int rc;
+ TALLOC_CTX *frame = NULL;
+
+ /*
+ * Allow caller to pass in NULL for stat pointer if
+ * required. This makes this call identical to
+ * smbc_readdirplus().
+ */
+
+ if (st == NULL) {
+ return SMBC_readdirplus_ctx(context, dir);
+ }
+
+ frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+ if (context == NULL || !context->internal->initialized) {
+ DBG_ERR("Invalid context in SMBC_readdirplus2_ctx()\n");
+ TALLOC_FREE(frame);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+ DBG_ERR("Invalid dir in SMBC_readdirplus2_ctx()\n");
+ TALLOC_FREE(frame);
+ errno = EBADF;
+ return NULL;
+ }
+
+ dp_list = dir->dirplus_next;
+ if (dp_list == NULL) {
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ino = (ino_t)dp_list->ino;
+
+ smb_finfo = dp_list->smb_finfo;
+ if (smb_finfo == NULL) {
+ TALLOC_FREE(frame);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ full_pathname = talloc_asprintf(frame,
+ "%s/%s",
+ dir->fname,
+ smb_finfo->name);
+ if (full_pathname == NULL) {
+ TALLOC_FREE(frame);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ rc = SMBC_parse_path(frame,
+ context,
+ full_pathname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ &options);
+ if (rc != 0) {
+ TALLOC_FREE(frame);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ setup_stat(st,
+ path,
+ smb_finfo->size,
+ smb_finfo->attrs,
+ ino,
+ dir->srv->dev,
+ smb_finfo->atime_ts,
+ smb_finfo->ctime_ts,
+ smb_finfo->mtime_ts);
+
+ TALLOC_FREE(full_pathname);
+
+ dir->dirplus_next = dir->dirplus_next->next;
+
+ /*
+ * If we are returning file entries, we
+ * have a duplicate list in dir_list
+ *
+ * Update dir_next also so readdir and
+ * readdirplus are kept in sync.
+ */
+ if (dir->dir_list) {
+ dir->dir_next = dir->dir_next->next;
+ }
+
+ TALLOC_FREE(frame);
+ return smb_finfo;
+}
+
+/*
+ * Routine to get directory entries
+ */
+
+int
+SMBC_getdents_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ struct smbc_dirent *dirp,
+ int count)
+{
+ int rem = count;
+ int reqd;
+ int maxlen;
+ char *ndir = (char *)dirp;
+ struct smbc_dir_list *dirlist;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Check that all is ok first ... */
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /*
+ * Now, retrieve the number of entries that will fit in what was passed
+ * We have to figure out if the info is in the list, or we need to
+ * send a request to the server to get the info.
+ */
+
+ while ((dirlist = dir->dir_next)) {
+ int ret;
+ struct smbc_dirent *dirent;
+ struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir;
+
+ if (!dirlist->dirent) {
+
+ errno = ENOENT; /* Bad error */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* Do urlencoding of next entry, if so selected */
+ dirent = &context->internal->dirent;
+ maxlen = sizeof(context->internal->_dirent_name);
+ ret = smbc_readdir_internal(context, dirent,
+ dirlist->dirent, maxlen);
+ if (ret == -1) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ reqd = dirent->dirlen;
+
+ if (rem < reqd) {
+
+ if (rem < count) { /* We managed to copy something */
+
+ errno = 0;
+ TALLOC_FREE(frame);
+ return count - rem;
+
+ }
+ else { /* Nothing copied ... */
+
+ errno = EINVAL; /* Not enough space ... */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ }
+
+ memcpy(currentEntry, dirent, reqd); /* Copy the data in ... */
+
+ currentEntry->comment = &currentEntry->name[0] +
+ dirent->namelen + 1;
+
+ ndir += reqd;
+ rem -= reqd;
+
+ /* Try and align the struct for the next entry
+ on a valid pointer boundary by appending zeros */
+ while((rem > 0) && ((uintptr_t)ndir & (sizeof(void*) - 1))) {
+ *ndir = '\0';
+ rem--;
+ ndir++;
+ currentEntry->dirlen++;
+ }
+
+ dir->dir_next = dirlist = dirlist -> next;
+
+ /*
+ * If we are returning file entries, we
+ * have a duplicate list in dirplus.
+ *
+ * Update dirplus_next also so readdir and
+ * readdirplus are kept in sync.
+ */
+ if (dir->dirplus_list != NULL) {
+ dir->dirplus_next = dir->dirplus_next->next;
+ }
+ }
+
+ TALLOC_FREE(frame);
+
+ if (rem == count)
+ return 0;
+ else
+ return count - rem;
+
+}
+
+/*
+ * Routine to create a directory ...
+ */
+
+int
+SMBC_mkdir_ctx(SMBCCTX *context,
+ const char *fname,
+ mode_t mode)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ uint16_t port = 0;
+ struct cli_state *targetcli = NULL;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_mkdir(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+
+ }
+
+ creds = context->internal->creds;
+
+ /*d_printf(">>>mkdir: resolving %s\n", path);*/
+ status = cli_resolve_path(frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/
+
+ status = cli_mkdir(targetcli, targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+/*
+ * Our list function simply checks to see if a directory is not empty
+ */
+
+static NTSTATUS
+rmdir_list_fn(struct file_info *finfo,
+ const char *mask,
+ void *state)
+{
+ if (strncmp(finfo->name, ".", 1) != 0 &&
+ strncmp(finfo->name, "..", 2) != 0) {
+ bool *smbc_rmdir_dirempty = (bool *)state;
+ *smbc_rmdir_dirempty = false;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ * Routine to remove a directory
+ */
+
+int
+SMBC_rmdir_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ uint16_t port = 0;
+ struct cli_state *targetcli = NULL;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_rmdir(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+
+ }
+
+ creds = context->internal->creds;
+
+ /*d_printf(">>>rmdir: resolving %s\n", path);*/
+ status = cli_resolve_path(frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/
+
+ status = cli_rmdir(targetcli, targetpath);
+
+ if (!NT_STATUS_IS_OK(status)) {
+
+ errno = cli_status_to_errno(status);
+
+ if (errno == EACCES) { /* Check if the dir empty or not */
+
+ /* Local storage to avoid buffer overflows */
+ char *lpath;
+ bool smbc_rmdir_dirempty = true;
+
+ lpath = talloc_asprintf(frame, "%s\\*",
+ targetpath);
+ if (!lpath) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ status = cli_list(targetcli, lpath,
+ FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
+ rmdir_list_fn,
+ &smbc_rmdir_dirempty);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Fix errno to ignore latest error ... */
+ DBG_INFO("cli_list returned an error: %s\n",
+ nt_errstr(status));
+ errno = EACCES;
+
+ }
+
+ if (smbc_rmdir_dirempty)
+ errno = EACCES;
+ else
+ errno = ENOTEMPTY;
+
+ }
+
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+
+}
+
+/*
+ * Routine to return the current directory position
+ */
+
+off_t
+SMBC_telldir_ctx(SMBCCTX *context,
+ SMBCFILE *dir)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* See if we're already at the end. */
+ if (dir->dir_next == NULL) {
+ /* We are. */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /*
+ * We return the pointer here as the offset
+ */
+ TALLOC_FREE(frame);
+ return (off_t)(long)dir->dir_next->dirent;
+}
+
+/*
+ * A routine to run down the list and see if the entry is OK
+ * Modifies the dir list and the dirplus list (if it exists)
+ * to point at the correct next entry on success.
+ */
+
+static bool update_dir_ents(SMBCFILE *dir, struct smbc_dirent *dirent)
+{
+ struct smbc_dir_list *tmp_dir = dir->dir_list;
+ struct smbc_dirplus_list *tmp_dirplus = dir->dirplus_list;
+
+ /*
+ * Run down the list looking for what we want.
+ * If we're enumerating files both dir_list
+ * and dirplus_list contain the same entry
+ * list, as they were seeded from the same
+ * cli_list callback.
+ *
+ * If we're enumerating servers then
+ * dirplus_list will be NULL, so don't
+ * update in that case.
+ */
+
+ while (tmp_dir != NULL) {
+ if (tmp_dir->dirent == dirent) {
+ dir->dir_next = tmp_dir;
+ if (tmp_dirplus != NULL) {
+ dir->dirplus_next = tmp_dirplus;
+ }
+ return true;
+ }
+ tmp_dir = tmp_dir->next;
+ if (tmp_dirplus != NULL) {
+ tmp_dirplus = tmp_dirplus->next;
+ }
+ }
+ return false;
+}
+
+/*
+ * Routine to seek on a directory
+ */
+
+int
+SMBC_lseekdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ off_t offset)
+{
+ long int l_offset = offset; /* Handle problems of size */
+ struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool ok;
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (dir->file != False) { /* FIXME, should be dir, perhaps */
+
+ errno = ENOTDIR;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* Now, check what we were passed and see if it is OK ... */
+
+ if (dirent == NULL) { /* Seek to the beginning of the list */
+
+ dir->dir_next = dir->dir_list;
+
+ /* Do the same for dirplus. */
+ dir->dirplus_next = dir->dirplus_list;
+
+ TALLOC_FREE(frame);
+ return 0;
+
+ }
+
+ if (offset == -1) { /* Seek to the end of the list */
+ dir->dir_next = NULL;
+
+ /* Do the same for dirplus. */
+ dir->dirplus_next = NULL;
+
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ /*
+ * Run down the list and make sure that the entry is OK.
+ * Update the position of both dir and dirplus lists.
+ */
+
+ ok = update_dir_ents(dir, dirent);
+ if (!ok) {
+ errno = EINVAL; /* Bad entry */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Routine to fstat a dir
+ */
+
+int
+SMBC_fstatdir_ctx(SMBCCTX *context,
+ SMBCFILE *dir,
+ struct stat *st)
+{
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* No code yet ... */
+ return 0;
+}
+
+int
+SMBC_chmod_ctx(SMBCCTX *context,
+ const char *fname,
+ mode_t newmode)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ char *path = NULL;
+ uint32_t attr;
+ uint16_t port = 0;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, (unsigned int)newmode));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ creds = context->internal->creds;
+
+ /*d_printf(">>>unlink: resolving %s\n", path);*/
+ status = cli_resolve_path(frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ attr = 0;
+
+ if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) attr |= FILE_ATTRIBUTE_READONLY;
+ if ((newmode & S_IXUSR) && lp_map_archive(-1)) attr |= FILE_ATTRIBUTE_ARCHIVE;
+ if ((newmode & S_IXGRP) && lp_map_system(-1)) attr |= FILE_ATTRIBUTE_SYSTEM;
+ if ((newmode & S_IXOTH) && lp_map_hidden(-1)) attr |= FILE_ATTRIBUTE_HIDDEN;
+
+ status = cli_setatr(targetcli, targetpath, attr, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+int
+SMBC_utimes_ctx(SMBCCTX *context,
+ const char *fname,
+ struct timeval *tbuf)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ struct timespec access_time, write_time;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool ok;
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (tbuf == NULL) {
+ access_time = write_time = timespec_current();
+ } else {
+ access_time = convert_timeval_to_timespec(tbuf[0]);
+ write_time = convert_timeval_to_timespec(tbuf[1]);
+ }
+
+ if (DEBUGLVL(4)) {
+ struct timeval_buf abuf, wbuf;
+
+ dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n",
+ fname,
+ timespec_string_buf(&access_time, false, &abuf),
+ timespec_string_buf(&write_time, false, &wbuf));
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ ok = SMBC_setatr(
+ context,
+ srv,
+ path,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
+ access_time,
+ write_time,
+ (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT },
+ 0);
+ if (!ok) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_setatr */
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Routine to unlink() a file
+ */
+
+int
+SMBC_unlink_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ uint16_t port = 0;
+ struct cli_state *targetcli = NULL;
+ SMBCSRV *srv = NULL;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* SMBC_server sets errno */
+
+ }
+
+ creds = context->internal->creds;
+
+ /*d_printf(">>>unlink: resolving %s\n", path);*/
+ status = cli_resolve_path(frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/
+
+ status = cli_unlink(
+ targetcli,
+ targetpath,
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!NT_STATUS_IS_OK(status)) {
+
+ errno = cli_status_to_errno(status);
+
+ if (errno == EACCES) { /* Check if the file is a directory */
+
+ int saverr = errno;
+ struct stat sb = {0};
+
+ status = SMBC_getatr(context, srv, path, &sb);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Hmmm, bad error ... What? */
+
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+
+ }
+ else {
+
+ if (S_ISDIR(sb.st_mode))
+ errno = EISDIR;
+ else
+ errno = saverr; /* Restore this */
+
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ TALLOC_FREE(frame);
+ return 0; /* Success ... */
+
+}
+
+/*
+ * Routine to rename() a file
+ */
+
+int
+SMBC_rename_ctx(SMBCCTX *ocontext,
+ const char *oname,
+ SMBCCTX *ncontext,
+ const char *nname)
+{
+ char *server1 = NULL;
+ char *share1 = NULL;
+ char *server2 = NULL;
+ char *share2 = NULL;
+ char *user1 = NULL;
+ char *user2 = NULL;
+ char *password1 = NULL;
+ char *password2 = NULL;
+ char *workgroup = NULL;
+ char *path1 = NULL;
+ char *path2 = NULL;
+ char *targetpath1 = NULL;
+ char *targetpath2 = NULL;
+ struct cli_state *targetcli1 = NULL;
+ struct cli_state *targetcli2 = NULL;
+ SMBCSRV *srv = NULL;
+ uint16_t port1 = 0;
+ uint16_t port2 = 0;
+ struct cli_credentials *ocreds = NULL;
+ struct cli_credentials *ncreds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!ocontext || !ncontext ||
+ !ocontext->internal->initialized ||
+ !ncontext->internal->initialized) {
+
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!oname || !nname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname));
+
+ if (SMBC_parse_path(frame,
+ ocontext,
+ oname,
+ &workgroup,
+ &server1,
+ &port1,
+ &share1,
+ &path1,
+ &user1,
+ &password1,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user1 || user1[0] == (char)0) {
+ user1 = talloc_strdup(frame, smbc_getUser(ocontext));
+ if (!user1) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ if (SMBC_parse_path(frame,
+ ncontext,
+ nname,
+ NULL,
+ &server2,
+ &port2,
+ &share2,
+ &path2,
+ &user2,
+ &password2,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user2 || user2[0] == (char)0) {
+ user2 = talloc_strdup(frame, smbc_getUser(ncontext));
+ if (!user2) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ if (strcmp(server1, server2) || strcmp(share1, share2) ||
+ strcmp(user1, user2)) {
+ /* Can't rename across file systems, or users?? */
+ errno = EXDEV;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ srv = SMBC_server(frame, ocontext, True,
+ server1, port1, share1, &workgroup, &user1, &password1);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+
+ /* set the credentials to make DFS work */
+ smbc_set_credentials_with_fallback(ocontext,
+ workgroup,
+ user1,
+ password1);
+
+ /*d_printf(">>>rename: resolving %s\n", path1);*/
+ ocreds = ocontext->internal->creds;
+
+ status = cli_resolve_path(frame, "",
+ ocreds,
+ srv->cli, path1, &targetcli1, &targetpath1);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path1);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* set the credentials to make DFS work */
+ smbc_set_credentials_with_fallback(ncontext,
+ workgroup,
+ user2,
+ password2);
+
+ /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/
+ /*d_printf(">>>rename: resolving %s\n", path2);*/
+ ncreds = ncontext->internal->creds;
+
+ status = cli_resolve_path(frame, "",
+ ncreds,
+ srv->cli, path2, &targetcli2, &targetpath2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path2);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/
+
+ if (strcmp(smbXcli_conn_remote_name(targetcli1->conn), smbXcli_conn_remote_name(targetcli2->conn)) ||
+ strcmp(targetcli1->share, targetcli2->share))
+ {
+ /* can't rename across file systems */
+ errno = EXDEV;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ status = cli_rename(targetcli1, targetpath1, targetpath2, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ int eno = cli_status_to_errno(status);
+
+ if (eno != EEXIST ||
+ !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN)) ||
+ !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1,
+ targetpath2, false))) {
+
+ errno = eno;
+ TALLOC_FREE(frame);
+ return -1;
+
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return 0; /* Success */
+}
+
+struct smbc_notify_cb_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ uint16_t fnum;
+ bool recursive;
+ uint32_t completion_filter;
+ unsigned callback_timeout_ms;
+ smbc_notify_callback_fn cb;
+ void *private_data;
+};
+
+static void smbc_notify_cb_got_changes(struct tevent_req *subreq);
+static void smbc_notify_cb_timedout(struct tevent_req *subreq);
+
+static struct tevent_req *smbc_notify_cb_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ uint16_t fnum, bool recursive, uint32_t completion_filter,
+ unsigned callback_timeout_ms,
+ smbc_notify_callback_fn cb, void *private_data)
+{
+ struct tevent_req *req, *subreq;
+ struct smbc_notify_cb_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct smbc_notify_cb_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->fnum = fnum;
+ state->recursive = recursive;
+ state->completion_filter = completion_filter;
+ state->callback_timeout_ms = callback_timeout_ms;
+ state->cb = cb;
+ state->private_data = private_data;
+
+ subreq = cli_notify_send(
+ state, state->ev, state->cli, state->fnum, 1000,
+ state->completion_filter, state->recursive);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
+
+ if (state->callback_timeout_ms == 0) {
+ return req;
+ }
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
+ state->callback_timeout_ms*1000));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
+
+ return req;
+}
+
+static void smbc_notify_cb_got_changes(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbc_notify_cb_state *state = tevent_req_data(
+ req, struct smbc_notify_cb_state);
+ uint32_t num_changes;
+ struct notify_change *changes;
+ NTSTATUS status;
+ int cb_ret;
+
+ status = cli_notify_recv(subreq, state, &num_changes, &changes);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ {
+ struct smbc_notify_callback_action actions[num_changes];
+ uint32_t i;
+
+ for (i=0; i<num_changes; i++) {
+ actions[i].action = changes[i].action;
+ actions[i].filename = changes[i].name;
+ }
+
+ cb_ret = state->cb(actions, num_changes, state->private_data);
+ }
+
+ TALLOC_FREE(changes);
+
+ if (cb_ret != 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = cli_notify_send(
+ state, state->ev, state->cli, state->fnum, 1000,
+ state->completion_filter, state->recursive);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req);
+}
+
+static void smbc_notify_cb_timedout(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbc_notify_cb_state *state = tevent_req_data(
+ req, struct smbc_notify_cb_state);
+ int cb_ret;
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+
+ cb_ret = state->cb(NULL, 0, state->private_data);
+ if (cb_ret != 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ tevent_timeval_current_ofs(state->callback_timeout_ms/1000,
+ state->callback_timeout_ms*1000));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req);
+}
+
+static NTSTATUS smbc_notify_cb_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static NTSTATUS smbc_notify_cb(struct cli_state *cli, uint16_t fnum,
+ bool recursive, uint32_t completion_filter,
+ unsigned callback_timeout_ms,
+ smbc_notify_callback_fn cb, void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = smbc_notify_cb_send(frame, ev, cli, fnum, recursive,
+ completion_filter,
+ callback_timeout_ms, cb, private_data);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = smbc_notify_cb_recv(req);
+ TALLOC_FREE(req);
+fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+int
+SMBC_notify_ctx(SMBCCTX *context, SMBCFILE *dir, smbc_bool recursive,
+ uint32_t completion_filter, unsigned callback_timeout_ms,
+ smbc_notify_callback_fn cb, void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct cli_state *cli;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *options = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16_t port;
+ NTSTATUS status;
+ uint16_t fnum;
+
+ if ((context == NULL) || !context->internal->initialized) {
+ TALLOC_FREE(frame);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!SMBC_dlist_contains(context->internal->files, dir)) {
+ TALLOC_FREE(frame);
+ errno = EBADF;
+ return -1;
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ dir->fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ &options)) {
+ DEBUG(4, ("no valid path\n"));
+ TALLOC_FREE(frame);
+ errno = EINVAL + 8194;
+ return -1;
+ }
+
+ DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' "
+ "path='%s' options='%s'\n",
+ dir->fname, server, share, path, options));
+
+ DEBUG(4, ("%s(%p, %d, %"PRIu32")\n", __func__, dir,
+ (int)recursive, completion_filter));
+
+ cli = dir->srv->cli;
+ status = cli_ntcreate(
+ cli, path, 0, FILE_READ_DATA, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+ FILE_OPEN, 0, 0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ status = smbc_notify_cb(cli, fnum, recursive != 0, completion_filter,
+ callback_timeout_ms, cb, private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_close(cli, fnum);
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ cli_close(cli, fnum);
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/libsmb/libsmb_file.c b/source3/libsmb/libsmb_file.c
new file mode 100644
index 0000000..ff18d56
--- /dev/null
+++ b/source3/libsmb/libsmb_file.c
@@ -0,0 +1,803 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/*
+ * Routine to open() a file ...
+ */
+
+SMBCFILE *
+SMBC_open_ctx(SMBCCTX *context,
+ const char *fname,
+ int flags,
+ mode_t mode)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ SMBCSRV *srv = NULL;
+ SMBCFILE *file = NULL;
+ uint16_t fd;
+ uint16_t port = 0;
+ NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+ if (!srv) {
+ if (errno == EPERM) errno = EACCES;
+ TALLOC_FREE(frame);
+ return NULL; /* SMBC_server sets errno */
+ }
+
+ /* Hmmm, the test for a directory is suspect here ... FIXME */
+
+ if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') {
+ status = NT_STATUS_OBJECT_PATH_INVALID;
+ } else {
+ struct cli_credentials *creds = NULL;
+
+ file = SMB_MALLOC_P(SMBCFILE);
+ if (!file) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(file);
+
+ creds = context->internal->creds;
+ /*d_printf(">>>open: resolving %s\n", path);*/
+ status = cli_resolve_path(
+ frame, "",
+ creds,
+ srv->cli, path, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ SAFE_FREE(file);
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/
+
+ status = cli_open(targetcli, targetpath, flags,
+ context->internal->share_mode, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+
+ /* Handle the error ... */
+
+ SAFE_FREE(file);
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return NULL;
+ }
+
+ /* Fill in file struct */
+
+ file->cli_fd = fd;
+ file->fname = SMB_STRDUP(fname);
+ file->srv = srv;
+ file->offset = 0;
+ file->file = True;
+ /*
+ * targetcli is either equal to srv->cli or
+ * is a subsidiary DFS connection. Either way
+ * file->cli_fd belongs to it so we must cache
+ * it for read/write/close, not re-resolve each time.
+ * Re-resolving is both slow and incorrect.
+ */
+ file->targetcli = targetcli;
+
+ DLIST_ADD(context->internal->files, file);
+
+ /*
+ * If the file was opened in O_APPEND mode, all write
+ * operations should be appended to the file. To do that,
+ * though, using this protocol, would require a getattrE()
+ * call for each and every write, to determine where the end
+ * of the file is. (There does not appear to be an append flag
+ * in the protocol.) Rather than add all of that overhead of
+ * retrieving the current end-of-file offset prior to each
+ * write operation, we'll assume that most append operations
+ * will continuously write, so we'll just set the offset to
+ * the end of the file now and hope that's adequate.
+ *
+ * Note to self: If this proves inadequate, and O_APPEND
+ * should, in some cases, be forced for each write, add a
+ * field in the context options structure, for
+ * "strict_append_mode" which would select between the current
+ * behavior (if FALSE) or issuing a getattrE() prior to each
+ * write and forcing the write to the end of the file (if
+ * TRUE). Adding that capability will likely require adding
+ * an "append" flag into the _SMBCFILE structure to track
+ * whether a file was opened in O_APPEND mode. -- djl
+ */
+ if (flags & O_APPEND) {
+ if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) {
+ (void) SMBC_close_ctx(context, file);
+ errno = ENXIO;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return file;
+ }
+
+ /* Check if opendir needed ... */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ file = smbc_getFunctionOpendir(context)(context, fname);
+ TALLOC_FREE(frame);
+ if (file == NULL) {
+ errno = cli_status_to_errno(status);
+ }
+ return file;
+ }
+
+ errno = EINVAL; /* FIXME, correct errno ? */
+ TALLOC_FREE(frame);
+ return NULL;
+}
+
+/*
+ * Routine to create a file
+ */
+
+SMBCFILE *
+SMBC_creat_ctx(SMBCCTX *context,
+ const char *path,
+ mode_t mode)
+{
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ return SMBC_open_ctx(context, path,
+ O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+/*
+ * Routine to read() a file ...
+ */
+
+ssize_t
+SMBC_read_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ void *buf,
+ size_t count)
+{
+ size_t ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ /*
+ * offset:
+ *
+ * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) --
+ * appears to pass file->offset (which is type off_t) differently than
+ * a local variable of type off_t. Using local variable "offset" in
+ * the call to cli_read() instead of file->offset fixes a problem
+ * retrieving data at an offset greater than 4GB.
+ */
+ off_t offset;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_read(%p, %zu)\n", file, count));
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ offset = file->offset;
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset,
+ count, &ret);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ file->offset += ret;
+
+ DEBUG(4, (" --> %zu\n", ret));
+
+ TALLOC_FREE(frame);
+ return ret; /* Success, ret bytes of data ... */
+}
+
+off_t
+SMBC_splice_ctx(SMBCCTX *context,
+ SMBCFILE *srcfile,
+ SMBCFILE *dstfile,
+ off_t count,
+ int (*splice_cb)(off_t n, void *priv),
+ void *priv)
+{
+ off_t written = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, srcfile)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, dstfile)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ status = cli_splice(srcfile->targetcli, dstfile->targetcli,
+ srcfile->cli_fd, dstfile->cli_fd,
+ count, srcfile->offset, dstfile->offset, &written,
+ splice_cb, priv);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ srcfile->offset += written;
+ dstfile->offset += written;
+
+ TALLOC_FREE(frame);
+ return written;
+}
+
+/*
+ * Routine to write() a file ...
+ */
+
+ssize_t
+SMBC_write_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ const void *buf,
+ size_t count)
+{
+ off_t offset;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ /* First check all pointers before dereferencing them */
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* Check that the buffer exists ... */
+
+ if (buf == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */
+
+ status = cli_writeall(file->targetcli, file->cli_fd,
+ 0, (const uint8_t *)buf, offset, count, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = map_errno_from_nt_status(status);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ file->offset += count;
+
+ TALLOC_FREE(frame);
+ return count; /* Success, 0 bytes of data ... */
+}
+
+/*
+ * Routine to close() a file ...
+ */
+
+int
+SMBC_close_ctx(SMBCCTX *context,
+ SMBCFILE *file)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* IS a dir ... */
+ if (!file->file) {
+ TALLOC_FREE(frame);
+ return smbc_getFunctionClosedir(context)(context, file);
+ }
+
+ status = cli_close(file->targetcli, file->cli_fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ SMBCSRV *srv;
+ DEBUG(3, ("cli_close failed on %s. purging server.\n",
+ file->fname));
+ /* Deallocate slot and remove the server
+ * from the server cache if unused */
+ srv = file->srv;
+ DLIST_REMOVE(context->internal->files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ smbc_getFunctionRemoveUnusedServer(context)(context, srv);
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ DLIST_REMOVE(context->internal->files, file);
+ SAFE_FREE(file->fname);
+ SAFE_FREE(file);
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Get info from an SMB server on a file. Use a qpathinfo call first
+ * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
+ */
+NTSTATUS
+SMBC_getatr(SMBCCTX * context,
+ SMBCSRV *srv,
+ const char *path,
+ struct stat *sb)
+{
+ char *fixedpath = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ uint32_t attr = 0;
+ off_t size = 0;
+ struct timespec create_time_ts = {0};
+ struct timespec access_time_ts = {0};
+ struct timespec write_time_ts = {0};
+ struct timespec change_time_ts = {0};
+ struct timespec w_time_ts = {0};
+ time_t write_time = 0;
+ SMB_INO_T ino = 0;
+ mode_t mode = S_IFREG;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* path fixup for . and .. */
+ if (ISDOT(path) || ISDOTDOT(path)) {
+ fixedpath = talloc_strdup(frame, "\\");
+ if (!fixedpath) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ fixedpath = talloc_strdup(frame, path);
+ if (!fixedpath) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ trim_string(fixedpath, NULL, "\\..");
+ trim_string(fixedpath, NULL, "\\.");
+ }
+ DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));
+
+ creds = context->internal->creds;
+
+ status = cli_resolve_path(frame, "",
+ creds,
+ srv->cli, fixedpath,
+ &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Couldn't resolve %s\n", path);
+ TALLOC_FREE(frame);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!srv->no_pathinfo2) {
+ bool not_supported_error = false;
+ status = cli_qpathinfo2(targetcli,
+ targetpath,
+ &create_time_ts,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &size,
+ &attr,
+ &ino,
+ &mode);
+ if (NT_STATUS_IS_OK(status)) {
+ goto setup_stat;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ not_supported_error = true;
+ }
+ if (!not_supported_error) {
+ /* "Normal error". Just return it to caller. */
+ TALLOC_FREE(frame);
+ return status;
+ }
+ }
+
+ srv->no_pathinfo2 = True;
+
+ if (!srv->no_pathinfo3) {
+ bool not_supported_error = false;
+ status = cli_qpathinfo3(targetcli,
+ targetpath,
+ &create_time_ts,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &size,
+ &attr,
+ &ino);
+ if (NT_STATUS_IS_OK(status)) {
+ goto setup_stat;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ not_supported_error = true;
+ }
+ if (!not_supported_error) {
+ /* "Normal error". Just return it to caller. */
+ TALLOC_FREE(frame);
+ return status;
+ }
+ }
+
+ srv->no_pathinfo3 = True;
+
+ /* if this is NT then don't bother with the getatr */
+ if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
+ goto all_failed;
+ }
+
+ status = cli_getatr(targetcli, targetpath, &attr, &size, &write_time);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto all_failed;
+ }
+ w_time_ts = convert_time_t_to_timespec(write_time);
+ access_time_ts = change_time_ts = write_time_ts = w_time_ts;
+
+setup_stat:
+ setup_stat(sb,
+ path,
+ size,
+ attr,
+ ino,
+ srv->dev,
+ access_time_ts,
+ change_time_ts,
+ write_time_ts);
+
+ if ((context->internal->posix_extensions) && (mode != S_IFREG)) {
+ sb->st_mode = (sb->st_mode & ~S_IFMT) | mode;
+ }
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+
+all_failed:
+ srv->no_pathinfo2 = False;
+ srv->no_pathinfo3 = False;
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*
+ * Set file info on an SMB server. Use setpathinfo call first. If that
+ * fails, use setattrE..
+ *
+ * Access and modification time parameters are always used and must be
+ * provided. Create time, if zero, will be determined from the actual create
+ * time of the file. If non-zero, the create time will be set as well.
+ *
+ * "attr" (attributes) parameter may be set to -1 if it is not to be set.
+ */
+bool
+SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path,
+ struct timespec create_time,
+ struct timespec access_time,
+ struct timespec write_time,
+ struct timespec change_time,
+ uint16_t attr)
+{
+ uint16_t fd;
+ uint32_t lattr = (uint32_t)attr;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (attr == (uint16_t)-1) {
+ /*
+ * External ABI only passes in
+ * 16-bits of attribute. Make
+ * sure we correctly map to
+ * (uint32_t)-1 meaning don't
+ * change attributes if attr was
+ * passed in as 16-bit -1.
+ */
+ lattr = (uint32_t)-1;
+ }
+
+
+ /*
+ * First, try setpathinfo (if qpathinfo succeeded), for it is the
+ * modern function for "new code" to be using, and it works given a
+ * filename rather than requiring that the file be opened to have its
+ * attributes manipulated.
+ */
+ if (srv->no_pathinfo ||
+ !NT_STATUS_IS_OK(cli_setpathinfo_ext(srv->cli, path,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ lattr))) {
+
+ /*
+ * setpathinfo is not supported; go to plan B.
+ *
+ * cli_setatr() does not work on win98, and it also doesn't
+ * support setting the access time (only the modification
+ * time), so in all cases, we open the specified file and use
+ * cli_setattrE() which should work on all OS versions, and
+ * supports both times.
+ */
+
+ /* Don't try {q,set}pathinfo() again, with this server */
+ srv->no_pathinfo = True;
+
+ /* Open the file */
+ status = cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return False;
+ }
+
+ /* Set the new attributes */
+ status = cli_setattrE(
+ srv->cli,
+ fd,
+ change_time.tv_sec,
+ access_time.tv_sec,
+ write_time.tv_sec);
+
+ /* Close the file */
+ cli_close(srv->cli, fd);
+
+ /*
+ * Unfortunately, setattrE() doesn't have a provision for
+ * setting the access attr (attributes). We'll have to try
+ * cli_setatr() for that, and with only this parameter, it
+ * seems to work on win98.
+ */
+ if (NT_STATUS_IS_OK(status) && attr != (uint16_t) -1) {
+ status = cli_setatr(srv->cli, path, (uint32_t)attr, 0);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return False;
+ }
+ }
+
+ TALLOC_FREE(frame);
+ return True;
+}
+
+/*
+ * A routine to lseek() a file
+ */
+
+off_t
+SMBC_lseek_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ off_t offset,
+ int whence)
+{
+ off_t size;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file->file) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1; /* Can't lseek a dir ... */
+ }
+
+ switch (whence) {
+ case SEEK_SET:
+ file->offset = offset;
+ break;
+ case SEEK_CUR:
+ file->offset += offset;
+ break;
+ case SEEK_END:
+ if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
+ file->targetcli, file->cli_fd, NULL,
+ &size, NULL, NULL, NULL, NULL,
+ NULL))) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ file->offset = size + offset;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
+
+ TALLOC_FREE(frame);
+ return file->offset;
+}
+
+
+/*
+ * Routine to truncate a file given by its file descriptor, to a specified size
+ */
+
+int
+SMBC_ftruncate_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ off_t length)
+{
+ off_t size = length;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file->file) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/libsmb/libsmb_misc.c b/source3/libsmb/libsmb_misc.c
new file mode 100644
index 0000000..4509895
--- /dev/null
+++ b/source3/libsmb/libsmb_misc.c
@@ -0,0 +1,46 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * check if an element is part of the list.
+ */
+bool SMBC_dlist_contains(SMBCFILE * list, SMBCFILE *p)
+{
+ if ((p == NULL) || (list == NULL)) {
+ return false;
+ }
+ do {
+ if (p == list) {
+ return true;
+ }
+ list = list->next;
+ } while (list != NULL);
+ return false;
+}
diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c
new file mode 100644
index 0000000..615de1c
--- /dev/null
+++ b/source3/libsmb/libsmb_path.c
@@ -0,0 +1,416 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * smbc_urldecode()
+ * and urldecode_talloc() (internal fn.)
+ *
+ * Convert strings of %xx to their single character equivalent. Each 'x' must
+ * be a valid hexadecimal digit, or that % sequence is left undecoded.
+ *
+ * dest may, but need not be, the same pointer as src.
+ *
+ * Returns the number of % sequences which could not be converted due to lack
+ * of two following hexadecimal digits.
+ */
+static int
+urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src)
+{
+ int old_length = strlen(src);
+ int i = 0;
+ int err_count = 0;
+ size_t newlen = 1;
+ char *p, *dest;
+
+ if (old_length == 0) {
+ return 0;
+ }
+
+ *pp_dest = NULL;
+ for (i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ uint8_t v;
+ bool ok = hex_byte(&src[i], &v);
+ if (ok) {
+ character = v;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ newlen++;
+ }
+
+ dest = talloc_array(ctx, char, newlen);
+ if (!dest) {
+ return err_count;
+ }
+
+ err_count = 0;
+ for (p = dest, i = 0; i < old_length; ) {
+ unsigned char character = src[i++];
+
+ if (character == '%') {
+ uint8_t v;
+ bool ok = hex_byte(&src[i], &v);
+ if (ok) {
+ character = v;
+ if (character == '\0') {
+ break; /* Stop at %00 */
+ }
+ i += 2;
+ } else {
+ err_count++;
+ }
+ }
+ *p++ = character;
+ }
+
+ *p = '\0';
+ *pp_dest = dest;
+ return err_count;
+}
+
+int
+smbc_urldecode(char *dest,
+ char *src,
+ size_t max_dest_len)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *pdest;
+ int ret = urldecode_talloc(frame, &pdest, src);
+
+ if (pdest) {
+ strlcpy(dest, pdest, max_dest_len);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/*
+ * smbc_urlencode()
+ *
+ * Convert any characters not specifically allowed in a URL into their %xx
+ * equivalent.
+ *
+ * Returns the remaining buffer length.
+ */
+int
+smbc_urlencode(char *dest,
+ char *src,
+ int max_dest_len)
+{
+ char hex[] = "0123456789ABCDEF";
+
+ for (; *src != '\0' && max_dest_len >= 3; src++) {
+
+ if ((*src < '0' &&
+ *src != '-' &&
+ *src != '.') ||
+ (*src > '9' &&
+ *src < 'A') ||
+ (*src > 'Z' &&
+ *src < 'a' &&
+ *src != '_') ||
+ (*src > 'z')) {
+ *dest++ = '%';
+ *dest++ = hex[(*src >> 4) & 0x0f];
+ *dest++ = hex[*src & 0x0f];
+ max_dest_len -= 3;
+ } else {
+ *dest++ = *src;
+ max_dest_len--;
+ }
+ }
+
+ if (max_dest_len <= 0) {
+ /* Ensure we return -1 if no null termination. */
+ return -1;
+ }
+
+ *dest++ = '\0';
+ max_dest_len--;
+
+ return max_dest_len;
+}
+
+/*
+ * Function to parse a path and turn it into components
+ *
+ * The general format of an SMB URI is explain in Christopher Hertel's CIFS
+ * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the
+ * general format ("smb:" only; we do not look for "cifs:").
+ *
+ *
+ * We accept:
+ * smb://[[[domain;]user[:password]@]server[:port][/share[/path[/file]]]]
+ * [?options]
+ *
+ * Meaning of URLs:
+ *
+ * smb:// Show all workgroups.
+ *
+ * The method of locating the list of workgroups varies
+ * depending upon the setting of the context variable
+ * context->options.browse_max_lmb_count. This value
+ * determines the maximum number of local master browsers to
+ * query for the list of workgroups. In order to ensure that
+ * a complete list of workgroups is obtained, all master
+ * browsers must be queried, but if there are many
+ * workgroups, the time spent querying can begin to add up.
+ * For small networks (not many workgroups), it is suggested
+ * that this variable be set to 0, indicating query all local
+ * master browsers. When the network has many workgroups, a
+ * reasonable setting for this variable might be around 3.
+ *
+ * smb://name/ if name<1D> or name<1B> exists, list servers in
+ * workgroup, else, if name<20> exists, list all shares
+ * for server ...
+ *
+ * If "options" are provided, this function returns the entire option list as a
+ * string, for later parsing by the caller. Note that currently, no options
+ * are supported.
+ */
+
+#define SMBC_PREFIX "smb:"
+
+int
+SMBC_parse_path(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *fname,
+ char **pp_workgroup,
+ char **pp_server,
+ uint16_t *p_port,
+ char **pp_share,
+ char **pp_path,
+ char **pp_user,
+ char **pp_password,
+ char **pp_options)
+{
+ char *s;
+ const char *p;
+ char *q, *r;
+ char *workgroup = NULL;
+ int len;
+
+ /* Ensure these returns are at least valid pointers. */
+ *pp_server = talloc_strdup(ctx, "");
+ *p_port = smbc_getPort(context);
+ *pp_share = talloc_strdup(ctx, "");
+ *pp_path = talloc_strdup(ctx, "");
+ *pp_user = talloc_strdup(ctx, "");
+ *pp_password = talloc_strdup(ctx, "");
+
+ if (!*pp_server || !*pp_share || !*pp_path ||
+ !*pp_user || !*pp_password) {
+ return -1;
+ }
+
+ /*
+ * Assume we won't find an authentication domain to parse, so default
+ * to the workgroup in the provided context.
+ */
+ if (pp_workgroup != NULL) {
+ *pp_workgroup =
+ talloc_strdup(ctx, smbc_getWorkgroup(context));
+ }
+
+ if (pp_options) {
+ *pp_options = talloc_strdup(ctx, "");
+ }
+ s = talloc_strdup(ctx, fname);
+
+ /* see if it has the right prefix */
+ len = strlen(SMBC_PREFIX);
+ if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) {
+ return -1; /* What about no smb: ? */
+ }
+
+ p = s + len;
+
+ /* Watch the test below, we are testing to see if we should exit */
+
+ if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) {
+ DEBUG(1, ("Invalid path (does not begin with smb://)\n"));
+ return -1;
+ }
+
+ p += 2; /* Skip the double slash */
+
+ /* See if any options were specified */
+ if ((q = strrchr(p, '?')) != NULL ) {
+ /* There are options. Null terminate here and point to them */
+ *q++ = '\0';
+
+ DEBUG(4, ("Found options '%s'\n", q));
+
+ /* Copy the options */
+ if (pp_options && *pp_options != NULL) {
+ TALLOC_FREE(*pp_options);
+ *pp_options = talloc_strdup(ctx, q);
+ }
+ }
+
+ if (*p == '\0') {
+ goto decoding;
+ }
+
+ if (*p == '/') {
+ *pp_server = talloc_strndup(
+ ctx, smbc_getWorkgroup(context), 16);
+ if (!*pp_server) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /*
+ * ok, its for us. Now parse out the server, share etc.
+ *
+ * However, we want to parse out [[domain;]user[:password]@] if it
+ * exists ...
+ */
+
+ /* check that '@' occurs before '/', if '/' exists at all */
+ q = strchr_m(p, '@');
+ r = strchr_m(p, '/');
+ if (q && (!r || q < r)) {
+ char *userinfo = NULL;
+ const char *u;
+
+ next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@");
+ if (!userinfo) {
+ return -1;
+ }
+ u = userinfo;
+
+ if (strchr_m(u, ';')) {
+ next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";");
+ if (!workgroup) {
+ return -1;
+ }
+ if (pp_workgroup) {
+ *pp_workgroup = workgroup;
+ }
+ }
+
+ if (strchr_m(u, ':')) {
+ next_token_no_ltrim_talloc(ctx, &u, pp_user, ":");
+ if (!*pp_user) {
+ return -1;
+ }
+ *pp_password = talloc_strdup(ctx, u);
+ if (!*pp_password) {
+ return -1;
+ }
+ } else {
+ *pp_user = talloc_strdup(ctx, u);
+ if (!*pp_user) {
+ return -1;
+ }
+ }
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_server, "/")) {
+ return -1;
+ }
+
+ /*
+ * Does *pp_server contain a ':' ? If so
+ * this denotes the port.
+ */
+ q = strchr_m(*pp_server, ':');
+ if (q != NULL) {
+ long int port;
+ char *endptr = NULL;
+ *q = '\0';
+ q++;
+ if (*q == '\0') {
+ /* Bad port. */
+ return -1;
+ }
+ port = strtol(q, &endptr, 10);
+ if (*endptr != '\0') {
+ /* Bad port. */
+ return -1;
+ }
+ *p_port = (uint16_t)port;
+ }
+
+ if (*p == (char)0) {
+ goto decoding; /* That's it ... */
+ }
+
+ if (!next_token_talloc(ctx, &p, pp_share, "/")) {
+ return -1;
+ }
+
+ /*
+ * Prepend a leading slash if there's a file path, as required by
+ * NetApp filers.
+ */
+ if (*p != '\0') {
+ *pp_path = talloc_asprintf(ctx,
+ "\\%s",
+ p);
+ } else {
+ *pp_path = talloc_strdup(ctx, "");
+ }
+ if (!*pp_path) {
+ return -1;
+ }
+ string_replace(*pp_path, '/', '\\');
+
+decoding:
+ (void) urldecode_talloc(ctx, pp_path, *pp_path);
+ (void) urldecode_talloc(ctx, pp_server, *pp_server);
+ (void) urldecode_talloc(ctx, pp_share, *pp_share);
+ (void) urldecode_talloc(ctx, pp_user, *pp_user);
+ (void) urldecode_talloc(ctx, pp_password, *pp_password);
+
+ if (!workgroup) {
+ workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
+ }
+ if (!workgroup) {
+ return -1;
+ }
+
+ /* set the credentials to make DFS work */
+ smbc_set_credentials_with_fallback(context,
+ workgroup,
+ *pp_user,
+ *pp_password);
+ return 0;
+}
+
diff --git a/source3/libsmb/libsmb_printjob.c b/source3/libsmb/libsmb_printjob.c
new file mode 100644
index 0000000..2b05dd0
--- /dev/null
+++ b/source3/libsmb/libsmb_printjob.c
@@ -0,0 +1,335 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/*
+ * Open a print file to be written to by other calls
+ */
+
+SMBCFILE *
+SMBC_open_print_job_ctx(SMBCCTX *context,
+ const char *fname)
+{
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *path = NULL;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ DEBUG(4, ("SMBC_open_print_job_ctx(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ NULL,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return NULL;
+ }
+
+ /* What if the path is empty, or the file exists? */
+
+ TALLOC_FREE(frame);
+ return smbc_getFunctionOpen(context)(context, fname, O_WRONLY, 666);
+}
+
+/*
+ * Routine to print a file on a remote server ...
+ *
+ * We open the file, which we assume to be on a remote server, and then
+ * copy it to a print file on the share specified by printq.
+ */
+
+int
+SMBC_print_file_ctx(SMBCCTX *c_file,
+ const char *fname,
+ SMBCCTX *c_print,
+ const char *printq)
+{
+ SMBCFILE *fid1;
+ SMBCFILE *fid2;
+ smbc_open_fn f_open1;
+ smbc_open_print_job_fn f_open_pj2;
+ int bytes;
+ int saverr;
+ int tot_bytes = 0;
+ char buf[4096];
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!c_file || !c_file->internal->initialized ||
+ !c_print || !c_print->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname && !printq) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* Try to open the file for reading ... */
+ f_open1 = smbc_getFunctionOpen(c_file);
+ if (f_open1 == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ fid1 = f_open1(c_file, fname, O_RDONLY, 0666);
+ if (fid1 == NULL) {
+ DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno));
+ TALLOC_FREE(frame);
+ return -1; /* smbc_open sets errno */
+ }
+
+ /* Now, try to open the printer file for writing */
+ f_open_pj2 = smbc_getFunctionOpenPrintJob(c_print);
+ if (f_open_pj2 == NULL) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ fid2 = f_open_pj2(c_print, printq);
+ if (fid2 == NULL) {
+ saverr = errno; /* Save errno */
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ errno = saverr;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ while ((bytes = smbc_getFunctionRead(c_file)(c_file, fid1,
+ buf, sizeof(buf))) > 0) {
+ tot_bytes += bytes;
+
+ if ((smbc_getFunctionWrite(c_print)(c_print, fid2,
+ buf, bytes)) < 0) {
+ saverr = errno;
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ smbc_getFunctionClose(c_print)(c_print, fid2);
+ errno = saverr;
+ }
+ }
+
+ saverr = errno;
+
+ smbc_getFunctionClose(c_file)(c_file, fid1);
+ smbc_getFunctionClose(c_print)(c_print, fid2);
+
+ if (bytes < 0) {
+ errno = saverr;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return tot_bytes;
+}
+
+/*
+ * Routine to list print jobs on a printer share ...
+ */
+
+int
+SMBC_list_print_jobs_ctx(SMBCCTX *context,
+ const char *fname,
+ smbc_list_print_job_fn fn)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ status = cli_print_queue(srv->cli,
+ (void (*)(struct print_job_info *))fn);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Delete a print job from a remote printer share
+ */
+
+int
+SMBC_unlink_print_job_ctx(SMBCCTX *context,
+ const char *fname,
+ int id)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ status = cli_printjob_del(srv->cli, id);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = cli_status_to_errno(status);
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c
new file mode 100644
index 0000000..69b51b8
--- /dev/null
+++ b/source3/libsmb/libsmb_server.c
@@ -0,0 +1,910 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+ Copyright (C) SATOH Fumiyasu <fumiyas@osstech.co.jp> 2009.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "libcli/security/security.h"
+#include "libsmb/nmblib.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/*
+ * Check a server for being alive and well.
+ * returns 0 if the server is in shape. Returns 1 on error
+ *
+ * Also usable outside libsmbclient to enable external cache
+ * to do some checks too.
+ */
+int
+SMBC_check_server(SMBCCTX * context,
+ SMBCSRV * server)
+{
+ time_t now;
+
+ if (!cli_state_is_connected(server->cli)) {
+ return 1;
+ }
+
+ now = time_mono(NULL);
+
+ if (server->last_echo_time == (time_t)0 ||
+ now > server->last_echo_time +
+ (server->cli->timeout/1000)) {
+ unsigned char data[16] = {0};
+ NTSTATUS status = cli_echo(server->cli,
+ 1,
+ data_blob_const(data, sizeof(data)));
+ if (!NT_STATUS_IS_OK(status)) {
+ bool ok = false;
+ /*
+ * Some SMB2 servers (not Samba or Windows)
+ * check the session status on SMB2_ECHO and return
+ * NT_STATUS_USER_SESSION_DELETED
+ * if the session was not set. That's OK, they still
+ * replied.
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13218
+ */
+ if (smbXcli_conn_protocol(server->cli->conn) >=
+ PROTOCOL_SMB2_02) {
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_USER_SESSION_DELETED)) {
+ ok = true;
+ }
+ }
+ /*
+ * Some NetApp servers return
+ * NT_STATUS_INVALID_PARAMETER.That's OK, they still
+ * replied.
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13007
+ */
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_INVALID_PARAMETER)) {
+ ok = true;
+ }
+ if (!ok) {
+ return 1;
+ }
+ }
+ server->last_echo_time = now;
+ }
+ return 0;
+}
+
+/*
+ * 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 usable 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;
+}
+
+/****************************************************************
+ * Call the auth_fn with fixed size (fstring) buffers.
+ ***************************************************************/
+static void
+SMBC_call_auth_fn(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *server,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ fstring workgroup = { 0 };
+ fstring username = { 0 };
+ fstring password = { 0 };
+ smbc_get_auth_data_with_context_fn auth_with_context_fn;
+
+ if (*pp_workgroup != NULL) {
+ strlcpy(workgroup, *pp_workgroup, sizeof(workgroup));
+ }
+ if (*pp_username != NULL) {
+ strlcpy(username, *pp_username, sizeof(username));
+ }
+ if (*pp_password != NULL) {
+ strlcpy(password, *pp_password, sizeof(password));
+ }
+
+ /* See if there's an authentication with context function provided */
+ auth_with_context_fn = smbc_getFunctionAuthDataWithContext(context);
+ if (auth_with_context_fn)
+ {
+ (* auth_with_context_fn)(context,
+ server, share,
+ workgroup, sizeof(workgroup),
+ username, sizeof(username),
+ password, sizeof(password));
+ }
+ else
+ {
+ smbc_getFunctionAuthData(context)(server, share,
+ workgroup, sizeof(workgroup),
+ username, sizeof(username),
+ password, sizeof(password));
+ }
+
+ TALLOC_FREE(*pp_workgroup);
+ TALLOC_FREE(*pp_username);
+ TALLOC_FREE(*pp_password);
+
+ *pp_workgroup = talloc_strdup(ctx, workgroup);
+ *pp_username = talloc_strdup(ctx, username);
+ *pp_password = talloc_strdup(ctx, password);
+}
+
+
+void
+SMBC_get_auth_data(const char *server, const char *share,
+ char *workgroup_buf, int workgroup_buf_len,
+ char *username_buf, int username_buf_len,
+ char *password_buf, int password_buf_len)
+{
+ /* Default function just uses provided data. Nothing to do. */
+}
+
+
+
+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 struct cli_credentials *SMBC_auth_credentials(TALLOC_CTX *mem_ctx,
+ SMBCCTX *context,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ struct cli_credentials *creds = NULL;
+ bool use_kerberos = false;
+ bool fallback_after_kerberos = false;
+ bool use_ccache = false;
+ bool pw_nt_hash = false;
+
+ use_kerberos = smbc_getOptionUseKerberos(context);
+ fallback_after_kerberos = smbc_getOptionFallbackAfterKerberos(context);
+ use_ccache = smbc_getOptionUseCCache(context);
+ pw_nt_hash = smbc_getOptionUseNTHash(context);
+
+ creds = cli_session_creds_init(mem_ctx,
+ username,
+ domain,
+ NULL, /* realm */
+ password,
+ use_kerberos,
+ fallback_after_kerberos,
+ use_ccache,
+ pw_nt_hash);
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ switch (context->internal->smb_encryption_level) {
+ case SMBC_ENCRYPTLEVEL_DEFAULT:
+ /* Use the config option */
+ break;
+ case SMBC_ENCRYPTLEVEL_NONE:
+ (void)cli_credentials_set_smb_encryption(
+ creds,
+ SMB_ENCRYPTION_OFF,
+ CRED_SPECIFIED);
+ break;
+ case SMBC_ENCRYPTLEVEL_REQUEST:
+ (void)cli_credentials_set_smb_encryption(
+ creds,
+ SMB_ENCRYPTION_DESIRED,
+ CRED_SPECIFIED);
+ break;
+ case SMBC_ENCRYPTLEVEL_REQUIRE:
+ default:
+ (void)cli_credentials_set_smb_encryption(
+ creds,
+ SMB_ENCRYPTION_REQUIRED,
+ CRED_SPECIFIED);
+ break;
+ }
+
+
+ return creds;
+}
+
+/*
+ * Connect to a server, possibly on an existing connection
+ *
+ * Here, what we want to do is: If the server and username
+ * match an existing connection, reuse that, otherwise, establish a
+ * new connection.
+ *
+ * If we have to create a new connection, call the auth_fn to get the
+ * info we need, unless the username and password were passed in.
+ */
+
+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 = NULL;
+ const char *password_used = NULL;
+ NTSTATUS status;
+ char *newserver, *newshare;
+ int flags = 0;
+ struct smbXcli_tcon *tcon = NULL;
+ int signing_state = SMB_SIGNING_DEFAULT;
+ struct cli_credentials *creds = NULL;
+
+ *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);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_shutdown(srv->cli);
+ errno = map_errno_from_nt_status(status);
+ srv->cli = NULL;
+ smbc_getFunctionRemoveCachedServer(context)(context,
+ srv);
+ srv = NULL;
+ goto not_found;
+ }
+
+ /* 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(srv->cli, &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(srv->cli, true);
+ } else {
+ cli_set_case_sensitive(srv->cli, false);
+ }
+ } else if (!is_ipc) {
+ DEBUG(4,
+ ("Case sensitive: %s\n",
+ (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
+ ? "True"
+ : "False")));
+ cli_set_case_sensitive(
+ srv->cli,
+ (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));
+ }
+ }
+ }
+
+ not_found:
+
+ /* 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 (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)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ DBG_ERR("NetBIOS support disabled, unable to connect\n");
+ }
+
+ 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(),
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_shutdown(c);
+ errno = map_errno_from_nt_status(status);
+ 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;
+ password_used = *pp_password;
+
+ creds = SMBC_auth_credentials(c,
+ context,
+ *pp_workgroup,
+ username_used,
+ password_used);
+ if (creds == NULL) {
+ cli_shutdown(c);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ status = cli_session_setup_creds(c, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+
+ /* Failed. Try an anonymous login, if allowed by flags. */
+ username_used = "";
+ password_used = "";
+
+ if (smbc_getOptionNoAutoAnonymousLogin(context) ||
+ !NT_STATUS_IS_OK(cli_session_setup_anon(c))) {
+
+ cli_shutdown(c);
+ errno = map_errno_from_nt_status(status);
+ 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,
+ creds)) {
+ 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_creds(c, share, "?????", creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ cli_shutdown(c);
+ errno = map_errno_from_nt_status(status);
+ 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);
+ }
+
+ /*
+ * 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_server(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)
+{
+ SMBCSRV *srv=NULL;
+ bool in_cache = false;
+
+ srv = SMBC_server_internal(ctx, context, connect_if_not_found,
+ server, port, share, pp_workgroup,
+ pp_username, pp_password, &in_cache);
+
+ if (!srv) {
+ return NULL;
+ }
+ if (in_cache) {
+ return srv;
+ }
+
+ /* Now add it to the cache (internal or external) */
+ /* Let the cache function set errno if it wants to */
+ errno = 0;
+ if (smbc_getFunctionAddCachedServer(context)(context, srv,
+ server, share,
+ *pp_workgroup,
+ *pp_username)) {
+ int saved_errno = errno;
+ DEBUG(3, (" Failed to add server to cache\n"));
+ errno = saved_errno;
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ SAFE_FREE(srv);
+ return NULL;
+ }
+
+ DEBUG(2, ("Server connect ok: //%s/%s: %p\n",
+ server, share, srv));
+
+ DLIST_ADD(context->internal->servers, srv);
+ return srv;
+}
+
+/*
+ * Connect to a server for getting/setting attributes, possibly on an existing
+ * connection. This works similarly to SMBC_server().
+ */
+SMBCSRV *
+SMBC_attr_server(TALLOC_CTX *ctx,
+ SMBCCTX *context,
+ const char *server,
+ uint16_t port,
+ const char *share,
+ char **pp_workgroup,
+ char **pp_username,
+ char **pp_password)
+{
+ int flags;
+ struct cli_state *ipc_cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS nt_status;
+ SMBCSRV *srv=NULL;
+ SMBCSRV *ipc_srv=NULL;
+
+ /*
+ * 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.
+ */
+ srv = SMBC_server(ctx, context, true, server, port, share,
+ pp_workgroup, pp_username, pp_password);
+ if (!srv) {
+ return NULL;
+ }
+ server = smbXcli_conn_remote_name(srv->cli->conn);
+ share = srv->cli->share;
+
+ /*
+ * See if we've already created this special connection. Reference
+ * our "special" share name '*IPC$', which is an impossible real share
+ * name due to the leading asterisk.
+ */
+ ipc_srv = SMBC_find_server(ctx, context, server, "*IPC$",
+ pp_workgroup, pp_username, pp_password);
+ if (!ipc_srv) {
+ struct cli_credentials *creds = NULL;
+
+ /* We didn't find a cached connection. Get the password */
+ if (!*pp_password || (*pp_password)[0] == '\0') {
+ /* ... then retrieve it now. */
+ SMBC_call_auth_fn(ctx, context, server, share,
+ pp_workgroup,
+ pp_username,
+ pp_password);
+ if (!*pp_workgroup || !*pp_username || !*pp_password) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ flags = 0;
+
+ creds = SMBC_auth_credentials(NULL,
+ context,
+ *pp_workgroup,
+ *pp_username,
+ *pp_password);
+ if (creds == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ nt_status = cli_full_connection_creds(&ipc_cli,
+ lp_netbios_name(), server,
+ NULL, 0, "IPC$", "?????",
+ creds,
+ flags);
+ if (! NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(creds);
+ DEBUG(1,("cli_full_connection failed! (%s)\n",
+ nt_errstr(nt_status)));
+ errno = ENOTSUP;
+ return NULL;
+ }
+ talloc_steal(ipc_cli, creds);
+
+ ipc_srv = SMB_MALLOC_P(SMBCSRV);
+ if (!ipc_srv) {
+ errno = ENOMEM;
+ cli_shutdown(ipc_cli);
+ return NULL;
+ }
+
+ ZERO_STRUCTP(ipc_srv);
+ DLIST_ADD(ipc_srv->cli, ipc_cli);
+
+ nt_status = cli_rpc_pipe_open_noauth(
+ ipc_srv->cli, &ndr_table_lsarpc, &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("cli_nt_session_open fail!\n"));
+ errno = ENOTSUP;
+ cli_shutdown(ipc_srv->cli);
+ free(ipc_srv);
+ return NULL;
+ }
+
+ /*
+ * Some systems don't support
+ * SEC_FLAG_MAXIMUM_ALLOWED, but NT sends 0x2000000
+ * so we might as well do it too.
+ */
+
+ nt_status = rpccli_lsa_open_policy(
+ pipe_hnd,
+ talloc_tos(),
+ True,
+ GENERIC_EXECUTE_ACCESS,
+ &ipc_srv->pol);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(ipc_srv->cli);
+ free(ipc_srv);
+ errno = cli_status_to_errno(nt_status);
+ return NULL;
+ }
+
+ /* now add it to the cache (internal or external) */
+
+ errno = 0; /* let cache function set errno if it likes */
+ if (smbc_getFunctionAddCachedServer(context)(context, ipc_srv,
+ server,
+ "*IPC$",
+ *pp_workgroup,
+ *pp_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;
+}
diff --git a/source3/libsmb/libsmb_setget.c b/source3/libsmb/libsmb_setget.c
new file mode 100644
index 0000000..7b140d6
--- /dev/null
+++ b/source3/libsmb/libsmb_setget.c
@@ -0,0 +1,1155 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#define __LIBSMBCLIENT_INTERNAL__
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "lib/param/param.h"
+
+
+/** Get the netbios name used for making connections */
+const char *
+smbc_getNetbiosName(SMBCCTX *c)
+{
+ return c->netbios_name;
+}
+
+/** Set the netbios name used for making connections */
+void
+smbc_setNetbiosName(SMBCCTX *c, const char *netbios_name)
+{
+ SAFE_FREE(c->netbios_name);
+ if (netbios_name) {
+ c->netbios_name = SMB_STRDUP(netbios_name);
+ }
+}
+
+/** Get the workgroup used for making connections */
+const char *
+smbc_getWorkgroup(SMBCCTX *c)
+{
+ return c->workgroup;
+}
+
+/** Set the workgroup used for making connections */
+void
+smbc_setWorkgroup(SMBCCTX *c, const char *workgroup)
+{
+ SAFE_FREE(c->workgroup);
+ if (workgroup) {
+ c->workgroup = SMB_STRDUP(workgroup);
+ }
+}
+
+/** Get the username used for making connections */
+const char *
+smbc_getUser(SMBCCTX *c)
+{
+ return c->user;
+}
+
+/** Set the username used for making connections */
+void
+smbc_setUser(SMBCCTX *c, const char *user)
+{
+ SAFE_FREE(c->user);
+ if (user) {
+ c->user = SMB_STRDUP(user);
+ }
+}
+
+/** Get the debug level */
+int
+smbc_getDebug(SMBCCTX *c)
+{
+ return c->debug;
+}
+
+/** Set the debug level */
+void
+smbc_setDebug(SMBCCTX *c, int debug)
+{
+ char buf[32];
+ TALLOC_CTX *frame = talloc_stackframe();
+ snprintf(buf, sizeof(buf), "%d", debug);
+ c->debug = debug;
+ lpcfg_set_cmdline(c->internal->lp_ctx, "log level", buf);
+ TALLOC_FREE(frame);
+}
+
+/** set callback function which will be called for logging */
+void
+smbc_setLogCallback(SMBCCTX *c, void *private_ptr,
+ smbc_debug_callback_fn fn)
+{
+ debug_set_callback(private_ptr, fn);
+}
+
+/** set configuration file */
+int smbc_setConfiguration(SMBCCTX *c, const char *file)
+{
+ bool ok;
+
+ ok = lp_load_client_no_reinit(file);
+ if (!ok) {
+ DBG_WARNING("Could not load config file: %s\n", file);
+ errno = ENOENT;
+ return -1;
+ }
+
+ DBG_NOTICE("Configuration loaded successfully: %s\n", file);
+ return 0;
+}
+/**
+ * Get the timeout used for waiting on connections and response data
+ * (in milliseconds)
+ */
+int
+smbc_getTimeout(SMBCCTX *c)
+{
+ return c->timeout;
+}
+
+/**
+ * Set the timeout used for waiting on connections and response data
+ * (in milliseconds)
+ */
+void
+smbc_setTimeout(SMBCCTX *c, int timeout)
+{
+ c->timeout = timeout;
+}
+
+/**
+ * Get the TCP port used to connect.
+ */
+uint16_t
+smbc_getPort(SMBCCTX *c)
+{
+ return c->internal->port;
+}
+
+/**
+ * Set the TCP port used to connect.
+ */
+void
+smbc_setPort(SMBCCTX *c, uint16_t port)
+{
+ c->internal->port = port;
+}
+
+
+/** Get whether to log to standard error instead of standard output */
+smbc_bool
+smbc_getOptionDebugToStderr(SMBCCTX *c)
+{
+ smbc_bool ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Because this is a global concept, it is better to check
+ * what is really set, rather than what we wanted set
+ * (particularly as you cannot go back to stdout). */
+ ret = debug_get_output_is_stderr();
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/** Set whether to log to standard error instead of standard output.
+ * This option is 'sticky' - once set to true, it cannot be set to
+ * false again, as it is global to the process, as once we have been
+ * told that it is not safe to safe to write to stdout, we shouldn't
+ * go back as we don't know it was this context that set it that way.
+ */
+void
+smbc_setOptionDebugToStderr(SMBCCTX *c, smbc_bool b)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ if (b) {
+ /*
+ * We do not have a unique per-thread debug state? For
+ * now, we'll just leave it up to the user. If any one
+ * context specifies debug to stderr then all will be (and
+ * will stay that way, as it is unsafe to flip back if
+ * stdout is in use for other things)
+ */
+ setup_logging("libsmbclient", DEBUG_STDERR);
+ }
+ TALLOC_FREE(frame);
+}
+
+/**
+ * Get whether to use new-style time attribute names, e.g. WRITE_TIME rather
+ * than the old-style names such as M_TIME. This allows also setting/getting
+ * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME
+ * was supposed to be CHANGE_TIME but was confused and sometimes referred to
+ * CREATE_TIME.)
+ */
+smbc_bool
+smbc_getOptionFullTimeNames(SMBCCTX *c)
+{
+ return c->internal->full_time_names;
+}
+
+/**
+ * Set whether to use new-style time attribute names, e.g. WRITE_TIME rather
+ * than the old-style names such as M_TIME. This allows also setting/getting
+ * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME
+ * was supposed to be CHANGE_TIME but was confused and sometimes referred to
+ * CREATE_TIME.)
+ */
+void
+smbc_setOptionFullTimeNames(SMBCCTX *c, smbc_bool b)
+{
+ c->internal->full_time_names = b;
+}
+
+/**
+ * Get the share mode to use for files opened with SMBC_open_ctx(). The
+ * default is SMBC_SHAREMODE_DENY_NONE.
+ */
+smbc_share_mode
+smbc_getOptionOpenShareMode(SMBCCTX *c)
+{
+ return c->internal->share_mode;
+}
+
+/**
+ * Set the share mode to use for files opened with SMBC_open_ctx(). The
+ * default is SMBC_SHAREMODE_DENY_NONE.
+ */
+void
+smbc_setOptionOpenShareMode(SMBCCTX *c, smbc_share_mode share_mode)
+{
+ c->internal->share_mode = share_mode;
+}
+
+/** Retrieve a previously set user data handle */
+void *
+smbc_getOptionUserData(SMBCCTX *c)
+{
+ return c->internal->user_data;
+}
+
+/** Save a user data handle */
+void
+smbc_setOptionUserData(SMBCCTX *c, void *user_data)
+{
+ c->internal->user_data = user_data;
+}
+
+/** Get the encoded value for encryption level. */
+smbc_smb_encrypt_level
+smbc_getOptionSmbEncryptionLevel(SMBCCTX *c)
+{
+ return c->internal->smb_encryption_level;
+}
+
+/** Set the encoded value for encryption level. */
+void
+smbc_setOptionSmbEncryptionLevel(SMBCCTX *c, smbc_smb_encrypt_level level)
+{
+ c->internal->smb_encryption_level = level;
+}
+
+/**
+ * Get whether to treat file names as case-sensitive if we can't determine
+ * when connecting to the remote share whether the file system is case
+ * sensitive. This defaults to FALSE since it's most likely that if we can't
+ * retrieve the file system attributes, it's a very old file system that does
+ * not support case sensitivity.
+ */
+smbc_bool
+smbc_getOptionCaseSensitive(SMBCCTX *c)
+{
+ return c->internal->case_sensitive;
+}
+
+/**
+ * Set whether to treat file names as case-sensitive if we can't determine
+ * when connecting to the remote share whether the file system is case
+ * sensitive. This defaults to FALSE since it's most likely that if we can't
+ * retrieve the file system attributes, it's a very old file system that does
+ * not support case sensitivity.
+ */
+void
+smbc_setOptionCaseSensitive(SMBCCTX *c, smbc_bool b)
+{
+ c->internal->case_sensitive = b;
+}
+
+/**
+ * Get from how many local master browsers should the list of workgroups be
+ * retrieved. It can take up to 12 minutes or longer after a server becomes a
+ * local master browser, for it to have the entire browse list (the list of
+ * workgroups/domains) from an entire network. Since a client never knows
+ * which local master browser will be found first, the one which is found
+ * first and used to retrieve a browse list may have an incomplete or empty
+ * browse list. By requesting the browse list from multiple local master
+ * browsers, a more complete list can be generated. For small networks (few
+ * workgroups), it is recommended that this value be set to 0, causing the
+ * browse lists from all found local master browsers to be retrieved and
+ * merged. For networks with many workgroups, a suitable value for this
+ * variable is probably somewhere around 3. (Default: 3).
+ */
+int
+smbc_getOptionBrowseMaxLmbCount(SMBCCTX *c)
+{
+ return c->options.browse_max_lmb_count;
+}
+
+/**
+ * Set from how many local master browsers should the list of workgroups be
+ * retrieved. It can take up to 12 minutes or longer after a server becomes a
+ * local master browser, for it to have the entire browse list (the list of
+ * workgroups/domains) from an entire network. Since a client never knows
+ * which local master browser will be found first, the one which is found
+ * first and used to retrieve a browse list may have an incomplete or empty
+ * browse list. By requesting the browse list from multiple local master
+ * browsers, a more complete list can be generated. For small networks (few
+ * workgroups), it is recommended that this value be set to 0, causing the
+ * browse lists from all found local master browsers to be retrieved and
+ * merged. For networks with many workgroups, a suitable value for this
+ * variable is probably somewhere around 3. (Default: 3).
+ */
+void
+smbc_setOptionBrowseMaxLmbCount(SMBCCTX *c, int count)
+{
+ c->options.browse_max_lmb_count = count;
+}
+
+/**
+ * Get whether to url-encode readdir entries.
+ *
+ * There is a difference in the desired return strings from
+ * smbc_readdir() depending upon whether the filenames are to
+ * be displayed to the user, or whether they are to be
+ * appended to the path name passed to smbc_opendir() to call
+ * a further smbc_ function (e.g. open the file with
+ * smbc_open()). In the former case, the filename should be
+ * in "human readable" form. In the latter case, the smbc_
+ * functions expect a URL which must be url-encoded. Those
+ * functions decode the URL. If, for example, smbc_readdir()
+ * returned a file name of "abc%20def.txt", passing a path
+ * with this file name attached to smbc_open() would cause
+ * smbc_open to attempt to open the file "abc def.txt" since
+ * the %20 is decoded into a space.
+ *
+ * Set this option to True if the names returned by
+ * smbc_readdir() should be url-encoded such that they can be
+ * passed back to another smbc_ call. Set it to False if the
+ * names returned by smbc_readdir() are to be presented to the
+ * user.
+ *
+ * For backwards compatibility, this option defaults to False.
+ */
+smbc_bool
+smbc_getOptionUrlEncodeReaddirEntries(SMBCCTX *c)
+{
+ return c->options.urlencode_readdir_entries;
+}
+
+/**
+ * Set whether to url-encode readdir entries.
+ *
+ * There is a difference in the desired return strings from
+ * smbc_readdir() depending upon whether the filenames are to
+ * be displayed to the user, or whether they are to be
+ * appended to the path name passed to smbc_opendir() to call
+ * a further smbc_ function (e.g. open the file with
+ * smbc_open()). In the former case, the filename should be
+ * in "human readable" form. In the latter case, the smbc_
+ * functions expect a URL which must be url-encoded. Those
+ * functions decode the URL. If, for example, smbc_readdir()
+ * returned a file name of "abc%20def.txt", passing a path
+ * with this file name attached to smbc_open() would cause
+ * smbc_open to attempt to open the file "abc def.txt" since
+ * the %20 is decoded into a space.
+ *
+ * Set this option to True if the names returned by
+ * smbc_readdir() should be url-encoded such that they can be
+ * passed back to another smbc_ call. Set it to False if the
+ * names returned by smbc_readdir() are to be presented to the
+ * user.
+ *
+ * For backwards compatibility, this option defaults to False.
+ */
+void
+smbc_setOptionUrlEncodeReaddirEntries(SMBCCTX *c, smbc_bool b)
+{
+ c->options.urlencode_readdir_entries = b;
+}
+
+/**
+ * Get whether to use the same connection for all shares on a server.
+ *
+ * Some Windows versions appear to have a limit to the number
+ * of concurrent SESSIONs and/or TREE CONNECTions. In
+ * one-shot programs (i.e. the program runs and then quickly
+ * ends, thereby shutting down all connections), it is
+ * probably reasonable to establish a new connection for each
+ * share. In long-running applications, the limitation can be
+ * avoided by using only a single connection to each server,
+ * and issuing a new TREE CONNECT when the share is accessed.
+ */
+smbc_bool
+smbc_getOptionOneSharePerServer(SMBCCTX *c)
+{
+ return c->options.one_share_per_server;
+}
+
+/**
+ * Set whether to use the same connection for all shares on a server.
+ *
+ * Some Windows versions appear to have a limit to the number
+ * of concurrent SESSIONs and/or TREE CONNECTions. In
+ * one-shot programs (i.e. the program runs and then quickly
+ * ends, thereby shutting down all connections), it is
+ * probably reasonable to establish a new connection for each
+ * share. In long-running applications, the limitation can be
+ * avoided by using only a single connection to each server,
+ * and issuing a new TREE CONNECT when the share is accessed.
+ */
+void
+smbc_setOptionOneSharePerServer(SMBCCTX *c, smbc_bool b)
+{
+ c->options.one_share_per_server = b;
+}
+
+/** Get whether to enable use of kerberos */
+smbc_bool
+smbc_getOptionUseKerberos(SMBCCTX *c)
+{
+ return c->flags & SMB_CTX_FLAG_USE_KERBEROS ? True : False;
+}
+
+/** Set whether to enable use of kerberos */
+void
+smbc_setOptionUseKerberos(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_USE_KERBEROS;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_USE_KERBEROS;
+ }
+}
+
+/** Get whether to fallback after kerberos */
+smbc_bool
+smbc_getOptionFallbackAfterKerberos(SMBCCTX *c)
+{
+ return c->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS ? True : False;
+}
+
+/** Set whether to fallback after kerberos */
+void
+smbc_setOptionFallbackAfterKerberos(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS;
+ }
+}
+
+/** Get whether to automatically select anonymous login */
+smbc_bool
+smbc_getOptionNoAutoAnonymousLogin(SMBCCTX *c)
+{
+ return c->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON ? True : False;
+}
+
+/** Set whether to automatically select anonymous login */
+void
+smbc_setOptionNoAutoAnonymousLogin(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+ } else {
+ c->flags &= ~SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON;
+ }
+}
+
+/** Get whether to enable use of the winbind ccache */
+smbc_bool
+smbc_getOptionUseCCache(SMBCCTX *c)
+{
+ return c->flags & SMB_CTX_FLAG_USE_CCACHE ? True : False;
+}
+
+/** Set whether to enable use of the winbind ccache */
+void
+smbc_setOptionUseCCache(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_USE_CCACHE;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_USE_CCACHE;
+ }
+}
+
+/** Get indication whether the password supplied is the NT hash */
+smbc_bool
+smbc_getOptionUseNTHash(SMBCCTX *c)
+{
+ return (c->flags & SMB_CTX_FLAG_USE_NT_HASH) != 0;
+}
+
+/** Set indication that the password supplied is the NT hash */
+void
+smbc_setOptionUseNTHash(SMBCCTX *c, smbc_bool b)
+{
+ if (b) {
+ c->flags |= SMB_CTX_FLAG_USE_NT_HASH;
+ } else {
+ c->flags &= ~SMB_CTX_FLAG_USE_NT_HASH;
+ }
+}
+
+smbc_bool
+smbc_setOptionProtocols(SMBCCTX *c,
+ const char *min_proto,
+ const char *max_proto)
+{
+ bool ok = true;
+
+ if (min_proto != NULL) {
+ ok = lpcfg_set_cmdline(c->internal->lp_ctx,
+ "client min protocol",
+ min_proto);
+ }
+
+ if (max_proto != NULL) {
+ ok &= lpcfg_set_cmdline(c->internal->lp_ctx,
+ "client max protocol",
+ max_proto);
+ }
+
+ return ok;
+}
+
+/** Get whether to enable POSIX extensions if available */
+smbc_bool
+smbc_getOptionPosixExtensions(SMBCCTX *c)
+{
+ return c->internal->posix_extensions;
+}
+
+/** Set whether to enable POSIX extensions if available */
+void
+smbc_setOptionPosixExtensions(SMBCCTX *c, smbc_bool b)
+{
+ c->internal->posix_extensions = b;
+}
+
+/** Get the function for obtaining authentication data */
+smbc_get_auth_data_fn
+smbc_getFunctionAuthData(SMBCCTX *c)
+{
+ smbc_get_auth_data_fn ret;
+ TALLOC_CTX *frame = talloc_stackframe();
+ ret = c->callbacks.auth_fn;
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/** Set the function for obtaining authentication data */
+void
+smbc_setFunctionAuthData(SMBCCTX *c, smbc_get_auth_data_fn fn)
+{
+ c->internal->auth_fn_with_context = NULL;
+ c->callbacks.auth_fn = fn;
+}
+
+/** Get the new-style authentication function which includes the context. */
+smbc_get_auth_data_with_context_fn
+smbc_getFunctionAuthDataWithContext(SMBCCTX *c)
+{
+ return c->internal->auth_fn_with_context;
+}
+
+/** Set the new-style authentication function which includes the context. */
+void
+smbc_setFunctionAuthDataWithContext(SMBCCTX *c,
+ smbc_get_auth_data_with_context_fn fn)
+{
+ c->callbacks.auth_fn = NULL;
+ c->internal->auth_fn_with_context = fn;
+}
+
+/** Get the function for checking if a server is still good */
+smbc_check_server_fn
+smbc_getFunctionCheckServer(SMBCCTX *c)
+{
+ return c->callbacks.check_server_fn;
+}
+
+/** Set the function for checking if a server is still good */
+void
+smbc_setFunctionCheckServer(SMBCCTX *c, smbc_check_server_fn fn)
+{
+ c->callbacks.check_server_fn = fn;
+}
+
+/** Get the function for removing a server if unused */
+smbc_remove_unused_server_fn
+smbc_getFunctionRemoveUnusedServer(SMBCCTX *c)
+{
+ return c->callbacks.remove_unused_server_fn;
+}
+
+/** Set the function for removing a server if unused */
+void
+smbc_setFunctionRemoveUnusedServer(SMBCCTX *c,
+ smbc_remove_unused_server_fn fn)
+{
+ c->callbacks.remove_unused_server_fn = fn;
+}
+
+/** Get the function for adding a cached server */
+smbc_add_cached_srv_fn
+smbc_getFunctionAddCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.add_cached_srv_fn;
+}
+
+/** Set the function for adding a cached server */
+void
+smbc_setFunctionAddCachedServer(SMBCCTX *c, smbc_add_cached_srv_fn fn)
+{
+ c->callbacks.add_cached_srv_fn = fn;
+}
+
+/** Get the function for server cache lookup */
+smbc_get_cached_srv_fn
+smbc_getFunctionGetCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.get_cached_srv_fn;
+}
+
+/** Set the function for server cache lookup */
+void
+smbc_setFunctionGetCachedServer(SMBCCTX *c, smbc_get_cached_srv_fn fn)
+{
+ c->callbacks.get_cached_srv_fn = fn;
+}
+
+/** Get the function for server cache removal */
+smbc_remove_cached_srv_fn
+smbc_getFunctionRemoveCachedServer(SMBCCTX *c)
+{
+ return c->callbacks.remove_cached_srv_fn;
+}
+
+/** Set the function for server cache removal */
+void
+smbc_setFunctionRemoveCachedServer(SMBCCTX *c,
+ smbc_remove_cached_srv_fn fn)
+{
+ c->callbacks.remove_cached_srv_fn = fn;
+}
+
+/**
+ * Get the function for server cache purging. This function tries to
+ * remove all cached servers (e.g. on disconnect)
+ */
+smbc_purge_cached_fn
+smbc_getFunctionPurgeCachedServers(SMBCCTX *c)
+{
+ return c->callbacks.purge_cached_fn;
+}
+
+/** Set the function to store private data of the server cache */
+void smbc_setServerCacheData(SMBCCTX *c, struct smbc_server_cache * cache)
+{
+ c->internal->server_cache = cache;
+}
+
+/** Get the function to store private data of the server cache */
+struct smbc_server_cache * smbc_getServerCacheData(SMBCCTX *c)
+{
+ return c->internal->server_cache;
+}
+
+
+/**
+ * Set the function for server cache purging. This function tries to
+ * remove all cached servers (e.g. on disconnect)
+ */
+void
+smbc_setFunctionPurgeCachedServers(SMBCCTX *c, smbc_purge_cached_fn fn)
+{
+ c->callbacks.purge_cached_fn = fn;
+}
+
+/**
+ * Callable functions for files.
+ */
+
+smbc_open_fn
+smbc_getFunctionOpen(SMBCCTX *c)
+{
+ return c->open;
+}
+
+void
+smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn)
+{
+ c->open = fn;
+}
+
+smbc_creat_fn
+smbc_getFunctionCreat(SMBCCTX *c)
+{
+ return c->creat;
+}
+
+void
+smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn fn)
+{
+ c->creat = fn;
+}
+
+smbc_read_fn
+smbc_getFunctionRead(SMBCCTX *c)
+{
+ return c->read;
+}
+
+void
+smbc_setFunctionRead(SMBCCTX *c, smbc_read_fn fn)
+{
+ c->read = fn;
+}
+
+smbc_write_fn
+smbc_getFunctionWrite(SMBCCTX *c)
+{
+ return c->write;
+}
+
+void
+smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn)
+{
+ c->write = fn;
+}
+
+smbc_splice_fn
+smbc_getFunctionSplice(SMBCCTX *c)
+{
+ return c->internal->smb.splice_fn;
+}
+
+void
+smbc_setFunctionSplice(SMBCCTX *c, smbc_splice_fn fn)
+{
+ c->internal->smb.splice_fn = fn;
+}
+
+smbc_unlink_fn
+smbc_getFunctionUnlink(SMBCCTX *c)
+{
+ return c->unlink;
+}
+
+void
+smbc_setFunctionUnlink(SMBCCTX *c, smbc_unlink_fn fn)
+{
+ c->unlink = fn;
+}
+
+smbc_rename_fn
+smbc_getFunctionRename(SMBCCTX *c)
+{
+ return c->rename;
+}
+
+void
+smbc_setFunctionRename(SMBCCTX *c, smbc_rename_fn fn)
+{
+ c->rename = fn;
+}
+
+smbc_lseek_fn
+smbc_getFunctionLseek(SMBCCTX *c)
+{
+ return c->lseek;
+}
+
+void
+smbc_setFunctionLseek(SMBCCTX *c, smbc_lseek_fn fn)
+{
+ c->lseek = fn;
+}
+
+smbc_stat_fn
+smbc_getFunctionStat(SMBCCTX *c)
+{
+ return c->stat;
+}
+
+void
+smbc_setFunctionStat(SMBCCTX *c, smbc_stat_fn fn)
+{
+ c->stat = fn;
+}
+
+smbc_fstat_fn
+smbc_getFunctionFstat(SMBCCTX *c)
+{
+ return c->fstat;
+}
+
+void
+smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn)
+{
+ c->fstat = fn;
+}
+
+smbc_statvfs_fn
+smbc_getFunctionStatVFS(SMBCCTX *c)
+{
+ return c->internal->posix_emu.statvfs_fn;
+}
+
+void
+smbc_setFunctionStatVFS(SMBCCTX *c, smbc_statvfs_fn fn)
+{
+ c->internal->posix_emu.statvfs_fn = fn;
+}
+
+smbc_fstatvfs_fn
+smbc_getFunctionFstatVFS(SMBCCTX *c)
+{
+ return c->internal->posix_emu.fstatvfs_fn;
+}
+
+void
+smbc_setFunctionFstatVFS(SMBCCTX *c, smbc_fstatvfs_fn fn)
+{
+ c->internal->posix_emu.fstatvfs_fn = fn;
+}
+
+smbc_ftruncate_fn
+smbc_getFunctionFtruncate(SMBCCTX *c)
+{
+ return c->internal->posix_emu.ftruncate_fn;
+}
+
+void
+smbc_setFunctionFtruncate(SMBCCTX *c, smbc_ftruncate_fn fn)
+{
+ c->internal->posix_emu.ftruncate_fn = fn;
+}
+
+smbc_close_fn
+smbc_getFunctionClose(SMBCCTX *c)
+{
+ return c->close_fn;
+}
+
+void
+smbc_setFunctionClose(SMBCCTX *c, smbc_close_fn fn)
+{
+ c->close_fn = fn;
+}
+
+
+/**
+ * Callable functions for directories.
+ */
+
+smbc_opendir_fn
+smbc_getFunctionOpendir(SMBCCTX *c)
+{
+ return c->opendir;
+}
+
+void
+smbc_setFunctionOpendir(SMBCCTX *c, smbc_opendir_fn fn)
+{
+ c->opendir = fn;
+}
+
+smbc_closedir_fn
+smbc_getFunctionClosedir(SMBCCTX *c)
+{
+ return c->closedir;
+}
+
+void
+smbc_setFunctionClosedir(SMBCCTX *c, smbc_closedir_fn fn)
+{
+ c->closedir = fn;
+}
+
+smbc_readdir_fn
+smbc_getFunctionReaddir(SMBCCTX *c)
+{
+ return c->readdir;
+}
+
+void
+smbc_setFunctionReaddir(SMBCCTX *c, smbc_readdir_fn fn)
+{
+ c->readdir = fn;
+}
+
+smbc_readdirplus_fn smbc_getFunctionReaddirPlus(SMBCCTX *c)
+{
+ return c->readdirplus;
+}
+
+void smbc_setFunctionReaddirPlus(SMBCCTX *c, smbc_readdirplus_fn fn)
+{
+ c->readdirplus = fn;
+}
+
+smbc_readdirplus2_fn smbc_getFunctionReaddirPlus2(SMBCCTX *c)
+{
+ return c->readdirplus2;
+}
+
+void smbc_setFunctionReaddirPlus2(SMBCCTX *c, smbc_readdirplus2_fn fn)
+{
+ c->readdirplus2 = fn;
+}
+
+smbc_getdents_fn
+smbc_getFunctionGetdents(SMBCCTX *c)
+{
+ return c->getdents;
+}
+
+void
+smbc_setFunctionGetdents(SMBCCTX *c, smbc_getdents_fn fn)
+{
+ c->getdents = fn;
+}
+
+smbc_mkdir_fn
+smbc_getFunctionMkdir(SMBCCTX *c)
+{
+ return c->mkdir;
+}
+
+void
+smbc_setFunctionMkdir(SMBCCTX *c, smbc_mkdir_fn fn)
+{
+ c->mkdir = fn;
+}
+
+smbc_rmdir_fn
+smbc_getFunctionRmdir(SMBCCTX *c)
+{
+ return c->rmdir;
+}
+
+void
+smbc_setFunctionRmdir(SMBCCTX *c, smbc_rmdir_fn fn)
+{
+ c->rmdir = fn;
+}
+
+smbc_telldir_fn
+smbc_getFunctionTelldir(SMBCCTX *c)
+{
+ return c->telldir;
+}
+
+void
+smbc_setFunctionTelldir(SMBCCTX *c, smbc_telldir_fn fn)
+{
+ c->telldir = fn;
+}
+
+smbc_lseekdir_fn
+smbc_getFunctionLseekdir(SMBCCTX *c)
+{
+ return c->lseekdir;
+}
+
+void
+smbc_setFunctionLseekdir(SMBCCTX *c, smbc_lseekdir_fn fn)
+{
+ c->lseekdir = fn;
+}
+
+smbc_fstatdir_fn
+smbc_getFunctionFstatdir(SMBCCTX *c)
+{
+ return c->fstatdir;
+}
+
+void
+smbc_setFunctionFstatdir(SMBCCTX *c, smbc_fstatdir_fn fn)
+{
+ c->fstatdir = fn;
+}
+
+smbc_notify_fn
+smbc_getFunctionNotify(SMBCCTX *c)
+{
+ return c->internal->smb.notify_fn;
+}
+
+void
+smbc_setFunctionNotify(SMBCCTX *c, smbc_notify_fn fn)
+{
+ c->internal->smb.notify_fn = fn;
+}
+
+
+/**
+ * Callable functions applicable to both files and directories.
+ */
+
+smbc_chmod_fn
+smbc_getFunctionChmod(SMBCCTX *c)
+{
+ return c->chmod;
+}
+
+void
+smbc_setFunctionChmod(SMBCCTX *c, smbc_chmod_fn fn)
+{
+ c->chmod = fn;
+}
+
+smbc_utimes_fn
+smbc_getFunctionUtimes(SMBCCTX *c)
+{
+ return c->utimes;
+}
+
+void
+smbc_setFunctionUtimes(SMBCCTX *c, smbc_utimes_fn fn)
+{
+ c->utimes = fn;
+}
+
+smbc_setxattr_fn
+smbc_getFunctionSetxattr(SMBCCTX *c)
+{
+ return c->setxattr;
+}
+
+void
+smbc_setFunctionSetxattr(SMBCCTX *c, smbc_setxattr_fn fn)
+{
+ c->setxattr = fn;
+}
+
+smbc_getxattr_fn
+smbc_getFunctionGetxattr(SMBCCTX *c)
+{
+ return c->getxattr;
+}
+
+void
+smbc_setFunctionGetxattr(SMBCCTX *c, smbc_getxattr_fn fn)
+{
+ c->getxattr = fn;
+}
+
+smbc_removexattr_fn
+smbc_getFunctionRemovexattr(SMBCCTX *c)
+{
+ return c->removexattr;
+}
+
+void
+smbc_setFunctionRemovexattr(SMBCCTX *c, smbc_removexattr_fn fn)
+{
+ c->removexattr = fn;
+}
+
+smbc_listxattr_fn
+smbc_getFunctionListxattr(SMBCCTX *c)
+{
+ return c->listxattr;
+}
+
+void
+smbc_setFunctionListxattr(SMBCCTX *c, smbc_listxattr_fn fn)
+{
+ c->listxattr = fn;
+}
+
+
+/**
+ * Callable functions related to printing
+ */
+
+smbc_print_file_fn
+smbc_getFunctionPrintFile(SMBCCTX *c)
+{
+ return c->print_file;
+}
+
+void
+smbc_setFunctionPrintFile(SMBCCTX *c, smbc_print_file_fn fn)
+{
+ c->print_file = fn;
+}
+
+smbc_open_print_job_fn
+smbc_getFunctionOpenPrintJob(SMBCCTX *c)
+{
+ return c->open_print_job;
+}
+
+void
+smbc_setFunctionOpenPrintJob(SMBCCTX *c,
+ smbc_open_print_job_fn fn)
+{
+ c->open_print_job = fn;
+}
+
+smbc_list_print_jobs_fn
+smbc_getFunctionListPrintJobs(SMBCCTX *c)
+{
+ return c->list_print_jobs;
+}
+
+void
+smbc_setFunctionListPrintJobs(SMBCCTX *c,
+ smbc_list_print_jobs_fn fn)
+{
+ c->list_print_jobs = fn;
+}
+
+smbc_unlink_print_job_fn
+smbc_getFunctionUnlinkPrintJob(SMBCCTX *c)
+{
+ return c->unlink_print_job;
+}
+
+void
+smbc_setFunctionUnlinkPrintJob(SMBCCTX *c,
+ smbc_unlink_print_job_fn fn)
+{
+ c->unlink_print_job = fn;
+}
+
diff --git a/source3/libsmb/libsmb_stat.c b/source3/libsmb/libsmb_stat.c
new file mode 100644
index 0000000..ae18f59
--- /dev/null
+++ b/source3/libsmb/libsmb_stat.c
@@ -0,0 +1,538 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "lib/util/time.h"
+
+/*
+ * Generate an inode number from file name for those things that need it
+ */
+
+static ino_t generate_inode(const char *name)
+{
+ if (name == NULL) {
+ return (ino_t)-1;
+ }
+ return (ino_t)str_checksum(name);
+}
+
+/*
+ * Routine to put basic stat info into a stat structure ... Used by stat and
+ * fstat below.
+ */
+
+void setup_stat(struct stat *st,
+ const char *fname,
+ off_t size,
+ int attr,
+ ino_t ino,
+ dev_t dev,
+ struct timespec access_time_ts,
+ struct timespec change_time_ts,
+ struct timespec write_time_ts)
+{
+ st->st_mode = 0;
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ st->st_mode = (S_IFDIR | 0555);
+ } else {
+ st->st_mode = (S_IFREG | 0444);
+ }
+
+ if (attr & FILE_ATTRIBUTE_ARCHIVE) {
+ st->st_mode |= S_IXUSR;
+ }
+ if (attr & FILE_ATTRIBUTE_SYSTEM) {
+ st->st_mode |= S_IXGRP;
+ }
+ if (attr & FILE_ATTRIBUTE_HIDDEN) {
+ st->st_mode |= S_IXOTH;
+ }
+ if (!(attr & FILE_ATTRIBUTE_READONLY)) {
+ st->st_mode |= S_IWUSR;
+ }
+
+ st->st_size = size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (size+511)/512;
+#endif
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ st->st_rdev = 0;
+#endif
+ st->st_uid = getuid();
+ st->st_gid = getgid();
+
+ if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+ st->st_nlink = 2;
+ } else {
+ st->st_nlink = 1;
+ }
+
+ if (ino != 0) {
+ st->st_ino = ino;
+ } else {
+ st->st_ino = generate_inode(fname);
+ }
+
+ st->st_dev = dev;
+
+ st->st_atime = access_time_ts.tv_sec;
+ set_atimensec(st, access_time_ts.tv_nsec);
+
+ st->st_ctime = change_time_ts.tv_sec;
+ set_ctimensec(st, change_time_ts.tv_nsec);
+
+ st->st_mtime = write_time_ts.tv_sec;
+ set_mtimensec(st, write_time_ts.tv_nsec);
+}
+
+void setup_stat_from_stat_ex(const struct stat_ex *stex,
+ const char *fname,
+ struct stat *st)
+{
+ st->st_atime = stex->st_ex_atime.tv_sec;
+ set_atimensec(st, stex->st_ex_atime.tv_nsec);
+
+ st->st_ctime = stex->st_ex_ctime.tv_sec;
+ set_ctimensec(st, stex->st_ex_ctime.tv_nsec);
+
+ st->st_mtime = stex->st_ex_mtime.tv_sec;
+ set_mtimensec(st, stex->st_ex_mtime.tv_nsec);
+
+ st->st_mode = stex->st_ex_mode;
+ st->st_size = stex->st_ex_size;
+#ifdef HAVE_STAT_ST_BLKSIZE
+ st->st_blksize = 512;
+#endif
+#ifdef HAVE_STAT_ST_BLOCKS
+ st->st_blocks = (st->st_size + 511) / 512;
+#endif
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ st->st_rdev = 0;
+#endif
+ st->st_uid = stex->st_ex_uid;
+ st->st_gid = stex->st_ex_gid;
+
+ st->st_nlink = stex->st_ex_nlink;
+
+ if (stex->st_ex_ino == 0) {
+ st->st_ino = 0;
+ if (fname != NULL) {
+ st->st_ino = generate_inode(fname);
+ }
+ } else {
+ st->st_ino = stex->st_ex_ino;
+ }
+
+ st->st_dev = stex->st_ex_dev;
+
+}
+
+/*
+ * Routine to stat a file given a name
+ */
+
+int
+SMBC_stat_ctx(SMBCCTX *context,
+ const char *fname,
+ struct stat *st)
+{
+ SMBCSRV *srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16_t port = 0;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_stat(%s)\n", fname));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ status = SMBC_getatr(context, srv, path, st);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+/*
+ * Routine to stat a file given an fd
+ */
+
+int
+SMBC_fstat_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ struct stat *st)
+{
+ struct timespec change_time_ts;
+ struct timespec access_time_ts;
+ struct timespec write_time_ts;
+ off_t size;
+ uint32_t attr;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *path = NULL;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ SMB_INO_T ino = 0;
+ uint16_t port = 0;
+ struct cli_credentials *creds = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!SMBC_dlist_contains(context->internal->files, file)) {
+ errno = EBADF;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!file->file) {
+ TALLOC_FREE(frame);
+ return smbc_getFunctionFstatdir(context)(context, file, st);
+ }
+
+ /*d_printf(">>>fstat: parsing %s\n", file->fname);*/
+ if (SMBC_parse_path(frame,
+ context,
+ file->fname,
+ NULL,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ creds = context->internal->creds;
+
+ /*d_printf(">>>fstat: resolving %s\n", path);*/
+ status = cli_resolve_path(frame, "",
+ creds,
+ file->srv->cli, path,
+ &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Could not resolve %s\n", path);
+ errno = ENOENT;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/
+
+ if (!NT_STATUS_IS_OK(cli_qfileinfo_basic(
+ targetcli, file->cli_fd, &attr, &size,
+ NULL,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ &ino))) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ setup_stat(st,
+ path,
+ size,
+ attr,
+ ino,
+ file->srv->dev,
+ access_time_ts,
+ change_time_ts,
+ write_time_ts);
+
+ TALLOC_FREE(frame);
+ return 0;
+}
+
+
+/*
+ * Routine to obtain file system information given a path
+ */
+int
+SMBC_statvfs_ctx(SMBCCTX *context,
+ char *path,
+ struct statvfs *st)
+{
+ int ret;
+ bool bIsDir;
+ struct stat statbuf;
+ SMBCFILE * pFile;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* Determine if the provided path is a file or a folder */
+ if (SMBC_stat_ctx(context, path, &statbuf) < 0) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ /* Is it a file or a directory? */
+ if (S_ISDIR(statbuf.st_mode)) {
+ /* It's a directory. */
+ if ((pFile = SMBC_opendir_ctx(context, path)) == NULL) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ bIsDir = true;
+ } else if (S_ISREG(statbuf.st_mode)) {
+ /* It's a file. */
+ if ((pFile = SMBC_open_ctx(context, path,
+ O_RDONLY, 0)) == NULL) {
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ bIsDir = false;
+ } else {
+ /* It's neither a file nor a directory. Not supported. */
+ TALLOC_FREE(frame);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ /* Now we have an open file handle, so just use SMBC_fstatvfs */
+ ret = SMBC_fstatvfs_ctx(context, pFile, st);
+
+ /* Close the file or directory */
+ if (bIsDir) {
+ SMBC_closedir_ctx(context, pFile);
+ } else {
+ SMBC_close_ctx(context, pFile);
+ }
+
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+
+/*
+ * Routine to obtain file system information given an fd
+ */
+
+int
+SMBC_fstatvfs_ctx(SMBCCTX *context,
+ SMBCFILE *file,
+ struct statvfs *st)
+{
+ unsigned long flags = 0;
+ uint32_t fs_attrs = 0;
+ struct cli_state *cli = file->srv->cli;
+ struct smbXcli_tcon *tcon;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ tcon = cli->smb2.tcon;
+ } else {
+ tcon = cli->smb1.tcon;
+ }
+
+ /* Initialize all fields (at least until we actually use them) */
+ ZERO_STRUCTP(st);
+
+ /*
+ * The state of each flag is such that the same bits are unset as
+ * would typically be unset on a local file system on a POSIX OS. Thus
+ * the bit is on, for example, only for case-insensitive file systems
+ * since most POSIX file systems are case sensitive and fstatvfs()
+ * would typically return zero in these bits on such a local file
+ * system.
+ */
+
+ /* See if the server has UNIX CIFS support */
+ if (! SERVER_HAS_UNIX_CIFS(cli)) {
+ uint64_t total_allocation_units;
+ uint64_t caller_allocation_units;
+ uint64_t actual_allocation_units;
+ uint64_t sectors_per_allocation_unit;
+ uint64_t bytes_per_sector;
+ NTSTATUS status;
+
+ /* Nope. If size data is available... */
+ status = cli_get_fs_full_size_info(cli,
+ &total_allocation_units,
+ &caller_allocation_units,
+ &actual_allocation_units,
+ &sectors_per_allocation_unit,
+ &bytes_per_sector);
+ if (NT_STATUS_IS_OK(status)) {
+
+ /* ... then provide it */
+ st->f_bsize =
+ (unsigned long) bytes_per_sector;
+#ifdef HAVE_FRSIZE
+ st->f_frsize =
+ (unsigned long) sectors_per_allocation_unit;
+#endif
+ st->f_blocks =
+ (fsblkcnt_t) total_allocation_units;
+ st->f_bfree =
+ (fsblkcnt_t) actual_allocation_units;
+ st->f_bavail =
+ (fsblkcnt_t) caller_allocation_units;
+ }
+
+ flags |= SMBC_VFS_FEATURE_NO_UNIXCIFS;
+ } else {
+ uint32_t optimal_transfer_size;
+ uint32_t block_size;
+ uint64_t total_blocks;
+ uint64_t blocks_available;
+ uint64_t user_blocks_available;
+ uint64_t total_file_nodes;
+ uint64_t free_file_nodes;
+ uint64_t fs_identifier;
+ NTSTATUS status;
+
+ /* Has UNIXCIFS. If POSIX filesystem info is available... */
+ status = cli_get_posix_fs_info(cli,
+ &optimal_transfer_size,
+ &block_size,
+ &total_blocks,
+ &blocks_available,
+ &user_blocks_available,
+ &total_file_nodes,
+ &free_file_nodes,
+ &fs_identifier);
+ if (NT_STATUS_IS_OK(status)) {
+
+ /* ... then what's provided here takes precedence. */
+ st->f_bsize =
+ (unsigned long) block_size;
+ st->f_blocks =
+ (fsblkcnt_t) total_blocks;
+ st->f_bfree =
+ (fsblkcnt_t) blocks_available;
+ st->f_bavail =
+ (fsblkcnt_t) user_blocks_available;
+ st->f_files =
+ (fsfilcnt_t) total_file_nodes;
+ st->f_ffree =
+ (fsfilcnt_t) free_file_nodes;
+#ifdef HAVE_FSID_INT
+ st->f_fsid =
+ (unsigned long) fs_identifier;
+#endif
+ }
+ }
+
+ /* See if the share is case sensitive */
+ if (!NT_STATUS_IS_OK(cli_get_fs_attr_info(cli, &fs_attrs))) {
+ /*
+ * 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)) {
+ flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
+ }
+ } else {
+ if (! (fs_attrs & FILE_CASE_SENSITIVE_SEARCH)) {
+ flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE;
+ }
+ }
+
+ /* See if DFS is supported */
+ if (smbXcli_conn_dfs_supported(cli->conn) &&
+ smbXcli_tcon_is_dfs_share(tcon))
+ {
+ flags |= SMBC_VFS_FEATURE_DFS;
+ }
+
+#if defined(HAVE_STATVFS_F_FLAG)
+ st->f_flag = flags;
+#elif defined(HAVE_STATVFS_F_FLAGS)
+ st->f_flags = flags;
+#endif
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/libsmb/libsmb_thread_impl.c b/source3/libsmb/libsmb_thread_impl.c
new file mode 100644
index 0000000..8b5d859
--- /dev/null
+++ b/source3/libsmb/libsmb_thread_impl.c
@@ -0,0 +1,127 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Derrell Lipman 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/**
+ * Initialize for an arbitrary thread implementation. The caller should
+ * provide, as parameters, pointers to functions to implement the requisite
+ * low-level thread functionality. A function must be provided for each
+ * parameter; none may be null.
+ *
+ * If the thread implementation is POSIX Threads (pthreads), then the much
+ * simpler smbc_thread_pthread() function may be used instead of this one.
+ *
+ * @param create_mutex
+ * Create a mutex. This function should expect three parameters: lockname,
+ * pplock, and location. It should create a unique mutex for each unique
+ * lockname it is provided, and return the mutex identifier in *pplock. The
+ * location parameter can be used for debugging, as it contains the
+ * compiler-provided __location__ of the call.
+ *
+ * @param destroy_mutex
+ * Destroy a mutex. This function should expect two parameters: plock and
+ * location. It should destroy the mutex associated with the identifier
+ * plock. The location parameter can be used for debugging, as it contains
+ * the compiler-provided __location__ of the call.
+ *
+ * @param lock_mutex
+ * Lock a mutex. This function should expect three parameters: plock,
+ * lock_type, and location. The mutex associated with identifier plock
+ * should be locked if lock_type is 1, and unlocked if lock_type is 2. The
+ * location parameter can be used for debugging, as it contains the
+ * compiler-provided __location__ of the call.
+ *
+ * @param create_tls
+ * Create thread local storage. This function should expect three
+ * parameters: keyname, ppkey, and location. It should allocate an
+ * implementation-specific amount of memory and assign the pointer to that
+ * allocated memory to *ppkey. The location parameter can be used for
+ * debugging, as it contains the compiler-provided __location__ of the
+ * call. This function should return 0 upon success, non-zero upon failure.
+ *
+ * @param destroy_tls
+ * Destroy thread local storage. This function should expect two parameters:
+ * ppkey and location. The ppkey parameter points to a variable containing a
+ * thread local storage key previously provided by the create_tls
+ * function. The location parameter can be used for debugging, as it
+ * contains the compiler-provided __location__ of the call.
+ *
+ * @param set_tls
+ * Set a thread local storage variable's value. This function should expect
+ * three parameters: pkey, pval, and location. The pkey parameter is a
+ * thread local storage key previously provided by the create_tls
+ * function. The (void *) pval parameter contains the value to be placed in
+ * the thread local storage variable identified by pkey. The location
+ * parameter can be used for debugging, as it contains the compiler-provided
+ * __location__ of the call. This function should return 0 upon success;
+ * non-zero otherwise.
+ *
+ * @param get_tls
+ * Retrieve a thread local storage variable's value. This function should
+ * expect two parameters: pkey and location. The pkey parameter is a thread
+ * local storage key previously provided by the create_tls function, and
+ * which has previously been used in a call to the set_tls function to
+ * initialize a thread local storage variable. The location parameter can be
+ * used for debugging, as it contains the compiler-provided __location__ of
+ * the call. This function should return the (void *) value stored in the
+ * variable identified by pkey.
+ *
+ * @return {void}
+ */
+void
+smbc_thread_impl(
+ /* Mutex functions. */
+ int (*create_mutex)(const char *lockname,
+ void **pplock,
+ const char *location),
+ void (*destroy_mutex)(void *plock,
+ const char *location),
+ int (*lock_mutex)(void *plock,
+ int lock_type,
+ const char *location),
+
+ /* Thread local storage. */
+ int (*create_tls)(const char *keyname,
+ void **ppkey,
+ const char *location),
+ void (*destroy_tls)(void **ppkey,
+ const char *location),
+ int (*set_tls)(void *pkey,
+ const void *pval,
+ const char *location),
+ void *(*get_tls)(void *pkey,
+ const char *location)
+ )
+{
+ static struct smb_thread_functions tf;
+
+ tf.create_mutex = create_mutex;
+ tf.destroy_mutex = destroy_mutex;
+ tf.lock_mutex = lock_mutex;
+ tf.create_tls = create_tls;
+ tf.destroy_tls = destroy_tls;
+ tf.set_tls = set_tls;
+ tf.get_tls = get_tls;
+
+ smb_thread_set_functions(&tf);
+}
diff --git a/source3/libsmb/libsmb_thread_posix.c b/source3/libsmb/libsmb_thread_posix.c
new file mode 100644
index 0000000..36afc02
--- /dev/null
+++ b/source3/libsmb/libsmb_thread_posix.c
@@ -0,0 +1,52 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Derrell Lipman 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+
+
+/* Get rid of the malloc checker */
+#ifdef malloc
+#undef malloc
+#endif
+
+/*
+ * Define the functions which implement the pthread interface
+ */
+SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf);
+
+
+/**
+ * Initialize for threads using the Posix Threads (pthread)
+ * implementation. This is a built-in implementation, avoiding the need to
+ * implement the component functions of the thread interface. If this function
+ * is used, it is not necessary to call smbc_thread_impl().
+ *
+ * @return {void}
+ */
+void
+smbc_thread_posix(void)
+{
+ smb_thread_set_functions(&tf);
+}
diff --git a/source3/libsmb/libsmb_xattr.c b/source3/libsmb/libsmb_xattr.c
new file mode 100644
index 0000000..dcb2f9e
--- /dev/null
+++ b/source3/libsmb/libsmb_xattr.c
@@ -0,0 +1,2406 @@
+/*
+ Unix SMB/Netbios implementation.
+ SMB client library implementation
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Richard Sharpe 2000, 2002
+ Copyright (C) John Terpstra 2000
+ Copyright (C) Tom Jansen (Ninja ISD) 2002
+ Copyright (C) Derrell Lipman 2003-2008
+ Copyright (C) Jeremy Allison 2007, 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "libsmbclient.h"
+#include "libsmb_internal.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "rpc_client/rpc_client.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../libcli/security/security.h"
+#include "lib/util/string_wrappers.h"
+
+/*
+ * Find an lsa pipe handle associated with a cli struct.
+ */
+static struct rpc_pipe_client *
+find_lsa_pipe_hnd(struct cli_state *ipc_cli)
+{
+ struct rpc_pipe_client *pipe_hnd;
+
+ for (pipe_hnd = ipc_cli->pipe_list;
+ pipe_hnd;
+ pipe_hnd = pipe_hnd->next) {
+ if (ndr_syntax_id_equal(&pipe_hnd->abstract_syntax,
+ &ndr_table_lsarpc.syntax_id)) {
+ return pipe_hnd;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Sort ACEs according to the documentation at
+ * http://support.microsoft.com/kb/269175, at least as far as it defines the
+ * order.
+ */
+
+static int
+ace_compare(struct security_ace *ace1,
+ struct security_ace *ace2)
+{
+ bool b1;
+ bool b2;
+
+ /* If the ACEs are equal, we have nothing more to do. */
+ if (security_ace_equal(ace1, ace2)) {
+ return 0;
+ }
+
+ /* Inherited follow non-inherited */
+ b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
+ b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * What shall we do with AUDITs and ALARMs? It's undefined. We'll
+ * sort them after DENY and ALLOW.
+ */
+ b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_DENIED &&
+ ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_DENIED &&
+ ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /* Allowed ACEs follow denied ACEs */
+ b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
+ ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
+ b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED ||
+ ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * ACEs applying to an entity's object follow those applying to the
+ * entity itself
+ */
+ b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+ ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT);
+ if (b1 != b2) {
+ return (b1 ? 1 : -1);
+ }
+
+ /*
+ * If we get this far, the ACEs are similar as far as the
+ * characteristics we typically care about (those defined by the
+ * referenced MS document). We'll now sort by characteristics that
+ * just seems reasonable.
+ */
+
+ if (ace1->type != ace2->type) {
+ return ace2->type - ace1->type;
+ }
+
+ if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) {
+ return dom_sid_compare(&ace1->trustee, &ace2->trustee);
+ }
+
+ if (ace1->flags != ace2->flags) {
+ return ace1->flags - ace2->flags;
+ }
+
+ if (ace1->access_mask != ace2->access_mask) {
+ return ace1->access_mask - ace2->access_mask;
+ }
+
+ if (ace1->size != ace2->size) {
+ return ace1->size - ace2->size;
+ }
+
+ return memcmp(ace1, ace2, sizeof(struct security_ace));
+}
+
+
+static void
+sort_acl(struct security_acl *the_acl)
+{
+ uint32_t i;
+ if (!the_acl) return;
+
+ TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (security_ace_equal(&the_acl->aces[i-1],
+ &the_acl->aces[i])) {
+ ARRAY_DEL_ELEMENT(
+ the_acl->aces, i, the_acl->num_aces);
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void
+convert_sid_to_string(struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ fstring str,
+ bool numeric,
+ struct dom_sid *sid)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ enum lsa_SidType *types = NULL;
+ struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
+ TALLOC_CTX *ctx;
+
+ sid_to_fstring(str, sid);
+
+ if (numeric) {
+ return; /* no lookup desired */
+ }
+
+ if (!pipe_hnd) {
+ return;
+ }
+
+ /* Ask LSA to convert the sid to a name */
+
+ ctx = talloc_stackframe();
+
+ if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx,
+ pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ TALLOC_FREE(ctx);
+ return;
+ }
+
+ /* Converted OK */
+
+ fstr_sprintf(str, "%s%s%s",
+ domains[0], lp_winbind_separator(), names[0]);
+
+ TALLOC_FREE(ctx);
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static bool
+convert_string_to_sid(struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ bool numeric,
+ struct dom_sid *sid,
+ const char *str)
+{
+ enum lsa_SidType *types = NULL;
+ struct dom_sid *sids = NULL;
+ bool result = True;
+ TALLOC_CTX *ctx = NULL;
+ struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli);
+
+ if (!pipe_hnd) {
+ return False;
+ }
+
+ if (numeric) {
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ result = False;
+ goto done;
+ }
+
+ ctx = talloc_stackframe();
+ if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx,
+ pol, 1, &str,
+ NULL, 1, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+done:
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+
+/* parse an struct security_ace in the same format as print_ace() */
+static bool
+parse_ace(struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ struct security_ace *ace,
+ bool numeric,
+ char *str)
+{
+ char *p;
+ const char *cp;
+ char *tok;
+ unsigned int atype;
+ unsigned int aflags;
+ unsigned int amask;
+ struct dom_sid sid;
+ uint32_t mask;
+ struct perm_value {
+ const char perm[7];
+ uint32_t mask;
+ };
+ size_t i;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* These values discovered by inspection */
+ static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ };
+
+ static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ };
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 &&
+ convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ cp = p;
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token_talloc(frame, &cp, &tok, "/") &&
+ sscanf(tok, "%u", &aflags))) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%u", &amask) != 1) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ goto done;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(standard_values); i++) {
+ const struct perm_value *v = &standard_values[i];
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ bool found = False;
+
+ for (i = 0; i < ARRAY_SIZE(special_values); i++) {
+ const struct perm_value *v = &special_values[i];
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+ p++;
+ }
+
+ if (*p) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+done:
+ mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ TALLOC_FREE(frame);
+ return true;
+}
+
+/* add an struct security_ace to a list of struct security_aces in a struct security_acl */
+static bool
+add_ace(struct security_acl **the_acl,
+ const struct security_ace *ace,
+ TALLOC_CTX *ctx)
+{
+ struct security_acl *acl = *the_acl;
+
+ if (acl == NULL) {
+ acl = make_sec_acl(ctx, 3, 0, NULL);
+ if (acl == NULL) {
+ return false;
+ }
+ }
+
+ if (acl->num_aces == UINT32_MAX) {
+ return false;
+ }
+ ADD_TO_ARRAY(
+ acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
+ *the_acl = acl;
+ return True;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static struct security_descriptor *
+sec_desc_parse(TALLOC_CTX *ctx,
+ struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ bool numeric,
+ const char *str)
+{
+ const char *p = str;
+ char *tok;
+ struct security_descriptor *ret = NULL;
+ size_t sd_size;
+ struct dom_sid owner_sid = { .num_auths = 0 };
+ struct dom_sid group_sid = { .num_auths = 0 };
+ bool have_owner = false, have_group = false;
+ struct security_acl *dacl=NULL;
+ int revision=1;
+
+ while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
+
+ if (strncasecmp_m(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"OWNER:", 6) == 0) {
+ if (have_owner) {
+ DEBUG(5,("OWNER specified more than once!\n"));
+ goto done;
+ }
+ if (!convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ &owner_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ goto done;
+ }
+ have_owner = true;
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"OWNER+:", 7) == 0) {
+ if (have_owner) {
+ DEBUG(5,("OWNER specified more than once!\n"));
+ goto done;
+ }
+ if (!convert_string_to_sid(ipc_cli, pol,
+ False,
+ &owner_sid, tok+7)) {
+ DEBUG(5, ("Failed to parse owner sid\n"));
+ goto done;
+ }
+ have_owner = true;
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"GROUP:", 6) == 0) {
+ if (have_group) {
+ DEBUG(5,("GROUP specified more than once!\n"));
+ goto done;
+ }
+ if (!convert_string_to_sid(ipc_cli, pol,
+ numeric,
+ &group_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ goto done;
+ }
+ have_group = true;
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"GROUP+:", 7) == 0) {
+ if (have_group) {
+ DEBUG(5,("GROUP specified more than once!\n"));
+ goto done;
+ }
+ if (!convert_string_to_sid(ipc_cli, pol,
+ False,
+ &group_sid, tok+6)) {
+ DEBUG(5, ("Failed to parse group sid\n"));
+ goto done;
+ }
+ have_group = true;
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"ACL:", 4) == 0) {
+ struct security_ace ace;
+ if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ goto done;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ goto done;
+ }
+ continue;
+ }
+
+ if (strncasecmp_m(tok,"ACL+:", 5) == 0) {
+ struct security_ace ace;
+ if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) {
+ DEBUG(5, ("Failed to parse ACL %s\n", tok));
+ goto done;
+ }
+ if(!add_ace(&dacl, &ace, ctx)) {
+ DEBUG(5, ("Failed to add ACL %s\n", tok));
+ goto done;
+ }
+ continue;
+ }
+
+ DEBUG(5, ("Failed to parse security descriptor\n"));
+ goto done;
+ }
+
+ ret = make_sec_desc(
+ ctx,
+ revision,
+ SEC_DESC_SELF_RELATIVE,
+ have_owner ? &owner_sid : NULL,
+ have_group ? &group_sid : NULL,
+ NULL,
+ dacl,
+ &sd_size);
+
+done:
+ return ret;
+}
+
+
+/* Obtain the current dos attributes */
+static struct DOS_ATTR_DESC *
+dos_attr_query(SMBCCTX *context,
+ TALLOC_CTX *ctx,
+ const char *filename,
+ SMBCSRV *srv)
+{
+ struct stat sb = {0};
+ struct DOS_ATTR_DESC *ret = NULL;
+ NTSTATUS status;
+
+ ret = talloc(ctx, struct DOS_ATTR_DESC);
+ if (!ret) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Obtain the DOS attributes */
+ status = SMBC_getatr(context, srv, filename, &sb);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("dos_attr_query Failed to query old attributes\n"));
+ TALLOC_FREE(ret);
+ errno = cli_status_to_errno(status);
+ return NULL;
+ }
+
+ ret->mode = sb.st_mode;
+ ret->size = sb.st_size;
+ ret->create_time = sb.st_ctime;
+ ret->access_time = sb.st_atime;
+ ret->write_time = sb.st_mtime;
+ ret->change_time = sb.st_mtime;
+ ret->inode = sb.st_ino;
+
+ return ret;
+}
+
+
+/* parse a ascii version of a security descriptor */
+static void
+dos_attr_parse(SMBCCTX *context,
+ struct DOS_ATTR_DESC *dad,
+ SMBCSRV *srv,
+ char *str)
+{
+ int n;
+ const char *p = str;
+ char *tok = NULL;
+ TALLOC_CTX *frame = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "CREATE_TIME";
+ attr_strings.access_time_attr = "ACCESS_TIME";
+ attr_strings.write_time_attr = "WRITE_TIME";
+ attr_strings.change_time_attr = "CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "A_TIME";
+ attr_strings.write_time_attr = "M_TIME";
+ attr_strings.change_time_attr = "C_TIME";
+ }
+
+ /* if this is to set the entire ACL... */
+ if (*str == '*') {
+ /* ... then increment past the first colon if there is one */
+ if ((p = strchr(str, ':')) != NULL) {
+ ++p;
+ } else {
+ p = str;
+ }
+ }
+
+ frame = talloc_stackframe();
+ while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) {
+ if (strncasecmp_m(tok, "MODE:", 5) == 0) {
+ long request = strtol(tok+5, NULL, 16);
+ if (request == 0) {
+ dad->mode =
+ (dad->mode & FILE_ATTRIBUTE_DIRECTORY)
+ ? FILE_ATTRIBUTE_DIRECTORY
+ : FILE_ATTRIBUTE_NORMAL;
+ } else {
+ dad->mode = request;
+ }
+ continue;
+ }
+
+ if (strncasecmp_m(tok, "SIZE:", 5) == 0) {
+ dad->size = (off_t)atof(tok+5);
+ continue;
+ }
+
+ n = strlen(attr_strings.access_time_attr);
+ if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) {
+ dad->access_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ n = strlen(attr_strings.change_time_attr);
+ if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) {
+ dad->change_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ n = strlen(attr_strings.write_time_attr);
+ if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) {
+ dad->write_time = (time_t)strtol(tok+n+1, NULL, 10);
+ continue;
+ }
+
+ if (attr_strings.create_time_attr != NULL) {
+ n = strlen(attr_strings.create_time_attr);
+ if (strncasecmp_m(tok, attr_strings.create_time_attr,
+ n) == 0) {
+ dad->create_time = (time_t)strtol(tok+n+1,
+ NULL, 10);
+ continue;
+ }
+ }
+
+ if (strncasecmp_m(tok, "INODE:", 6) == 0) {
+ dad->inode = (SMB_INO_T)atof(tok+6);
+ continue;
+ }
+ }
+ TALLOC_FREE(frame);
+}
+
+/*****************************************************
+ Retrieve the acls for a file.
+*******************************************************/
+
+static int
+cacl_get(SMBCCTX *context,
+ TALLOC_CTX *ctx,
+ SMBCSRV *srv,
+ struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ const char *filename,
+ const char *attr_name,
+ char *buf,
+ int bufsize)
+{
+ uint32_t i;
+ int n = 0;
+ int n_used;
+ bool all;
+ bool all_nt;
+ bool all_nt_acls;
+ bool all_dos;
+ bool some_nt;
+ bool some_dos;
+ bool exclude_nt_revision = False;
+ bool exclude_nt_owner = False;
+ bool exclude_nt_group = False;
+ bool exclude_nt_acl = False;
+ bool exclude_dos_mode = False;
+ bool exclude_dos_size = False;
+ bool exclude_dos_create_time = False;
+ bool exclude_dos_access_time = False;
+ bool exclude_dos_write_time = False;
+ bool exclude_dos_change_time = False;
+ bool exclude_dos_inode = False;
+ bool numeric = True;
+ bool determine_size = (bufsize == 0);
+ uint16_t fnum;
+ struct security_descriptor *sd;
+ fstring sidstr;
+ fstring name_sandbox;
+ char *name;
+ char *pExclude;
+ char *p;
+ struct cli_state *cli = srv->cli;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } excl_attr_strings;
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "CREATE_TIME";
+ attr_strings.access_time_attr = "ACCESS_TIME";
+ attr_strings.write_time_attr = "WRITE_TIME";
+ attr_strings.change_time_attr = "CHANGE_TIME";
+
+ excl_attr_strings.create_time_attr = "CREATE_TIME";
+ excl_attr_strings.access_time_attr = "ACCESS_TIME";
+ excl_attr_strings.write_time_attr = "WRITE_TIME";
+ excl_attr_strings.change_time_attr = "CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "A_TIME";
+ attr_strings.write_time_attr = "M_TIME";
+ attr_strings.change_time_attr = "C_TIME";
+
+ excl_attr_strings.create_time_attr = NULL;
+ excl_attr_strings.access_time_attr = "dos_attr.A_TIME";
+ excl_attr_strings.write_time_attr = "dos_attr.M_TIME";
+ excl_attr_strings.change_time_attr = "dos_attr.C_TIME";
+ }
+
+ /* Copy name so we can strip off exclusions (if any are specified) */
+ fstrcpy(name_sandbox, attr_name);
+
+ /* Ensure name is null terminated */
+ name_sandbox[sizeof(name_sandbox) - 1] = '\0';
+
+ /* Play in the sandbox */
+ name = name_sandbox;
+
+ /* If there are any exclusions, point to them and mask them from name */
+ if ((pExclude = strchr(name, '!')) != NULL)
+ {
+ *pExclude++ = '\0';
+ }
+
+ all = (strncasecmp_m(name, "system.*", 8) == 0);
+ all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0);
+ all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0);
+ all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0);
+ some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0);
+ some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0);
+ numeric = (* (name + strlen(name) - 1) != '+');
+
+ /* Look for exclusions from "all" requests */
+ if (all || all_nt || all_dos) {
+ /* Exclusions are delimited by '!' */
+ for (;
+ pExclude != NULL;
+ pExclude = (p == NULL ? NULL : p + 1)) {
+
+ /* Find end of this exclusion name */
+ if ((p = strchr(pExclude, '!')) != NULL)
+ {
+ *p = '\0';
+ }
+
+ /* Which exclusion name is this? */
+ if (strcasecmp_m(pExclude,
+ "nt_sec_desc.revision") == 0) {
+ exclude_nt_revision = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ "nt_sec_desc.owner") == 0) {
+ exclude_nt_owner = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ "nt_sec_desc.group") == 0) {
+ exclude_nt_group = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ "nt_sec_desc.acl") == 0) {
+ exclude_nt_acl = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ "dos_attr.mode") == 0) {
+ exclude_dos_mode = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ "dos_attr.size") == 0) {
+ exclude_dos_size = True;
+ }
+ else if (excl_attr_strings.create_time_attr != NULL &&
+ strcasecmp_m(pExclude,
+ excl_attr_strings.change_time_attr) == 0) {
+ exclude_dos_create_time = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ excl_attr_strings.access_time_attr) == 0) {
+ exclude_dos_access_time = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ excl_attr_strings.write_time_attr) == 0) {
+ exclude_dos_write_time = True;
+ }
+ else if (strcasecmp_m(pExclude,
+ excl_attr_strings.change_time_attr) == 0) {
+ exclude_dos_change_time = True;
+ }
+ else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) {
+ exclude_dos_inode = True;
+ }
+ else {
+ DEBUG(5, ("cacl_get received unknown exclusion: %s\n",
+ pExclude));
+ errno = ENOATTR;
+ return -1;
+ }
+ }
+ }
+
+ n_used = 0;
+
+ /*
+ * If we are (possibly) talking to an NT or new system and some NT
+ * attributes have been requested...
+ */
+ if (ipc_cli && (all || some_nt || all_nt_acls)) {
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ struct cli_credentials *creds = NULL;
+ NTSTATUS status;
+
+ /* Point to the portion after "system.nt_sec_desc." */
+ name += 19; /* if (all) this will be invalid but unused */
+
+ creds = context->internal->creds;
+
+ status = cli_resolve_path(
+ ctx, "",
+ creds,
+ cli, filename, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("cacl_get Could not resolve %s\n",
+ filename));
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* ... then obtain any NT attributes which were requested */
+ status = cli_ntcreate(
+ targetcli, /* cli */
+ targetpath, /* fname */
+ 0, /* CreatFlags */
+ READ_CONTROL_ACCESS, /* DesiredAccess */
+ 0, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0x0, /* CreateOptions */
+ 0x0, /* SecurityFlags */
+ &fnum, /* pfid */
+ NULL); /* cr */
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("cacl_get failed to open %s: %s\n",
+ targetpath, nt_errstr(status)));
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ status = cli_query_secdesc(targetcli, fnum, ctx, &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("cacl_get Failed to query old descriptor "
+ "of %s: %s\n",
+ targetpath, nt_errstr(status)));
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ cli_close(targetcli, fnum);
+
+ if (! exclude_nt_revision) {
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "REVISION:%d",
+ sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "REVISION:%d",
+ sd->revision);
+ }
+ } else if (strcasecmp_m(name, "revision") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%d",
+ sd->revision);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%d",
+ sd->revision);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_owner) {
+ /* Get owner and group sid */
+ if (sd->owner_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr,
+ numeric,
+ sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",OWNER:%s",
+ sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else if (sidstr[0] != '\0') {
+ n = snprintf(buf, bufsize,
+ ",OWNER:%s", sidstr);
+ }
+ } else if (strncasecmp_m(name, "owner", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize, "%s",
+ sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_group) {
+ if (sd->group_sid) {
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric,
+ sd->group_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, ",GROUP:%s",
+ sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else if (sidstr[0] != '\0') {
+ n = snprintf(buf, bufsize,
+ ",GROUP:%s", sidstr);
+ }
+ } else if (strncasecmp_m(name, "group", 5) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%s", sidstr);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%s", sidstr);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_nt_acl) {
+ /* Add aces to value buffer */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+
+ struct security_ace *ace = &sd->dacl->aces[i];
+ convert_sid_to_string(ipc_cli, pol,
+ sidstr, numeric,
+ &ace->trustee);
+
+ if (all || all_nt) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",ACL:"
+ "%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(
+ buf, bufsize,
+ ",ACL:%s:%d/%d/0x%08x",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ } else if ((strncasecmp_m(name, "acl", 3) == 0 &&
+ strcasecmp_m(name+3, sidstr) == 0) ||
+ (strncasecmp_m(name, "acl+", 4) == 0 &&
+ strcasecmp_m(name+4, sidstr) == 0)) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%d/%d/0x%08x",
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ } else if (all_nt_acls) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%s%s:%d/%d/0x%08x",
+ i ? "," : "",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%s%s:%d/%d/0x%08x",
+ i ? "," : "",
+ sidstr,
+ ace->type,
+ ace->flags,
+ ace->access_mask);
+ }
+ }
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+ }
+
+ /* Restore name pointer to its original value */
+ name -= 19;
+ }
+
+ if (all || some_dos) {
+ struct stat sb = {0};
+ time_t create_time = (time_t)0;
+ time_t write_time = (time_t)0;
+ time_t access_time = (time_t)0;
+ time_t change_time = (time_t)0;
+ off_t size = 0;
+ uint16_t mode = 0;
+ SMB_INO_T ino = 0;
+ NTSTATUS status;
+
+ /* Point to the portion after "system.dos_attr." */
+ name += 16; /* if (all) this will be invalid but unused */
+
+ /* Obtain the DOS attributes */
+ status = SMBC_getatr(context, srv, filename, &sb);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = cli_status_to_errno(status);
+ return -1;
+ }
+
+ create_time = sb.st_ctime;
+ access_time = sb.st_atime;
+ write_time = sb.st_mtime;
+ change_time = sb.st_mtime;
+ size = sb.st_size;
+ mode = sb.st_mode;
+ ino = sb.st_ino;
+
+ if (! exclude_dos_mode) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ "%sMODE:0x%x",
+ (ipc_cli &&
+ (all || some_nt)
+ ? ","
+ : ""),
+ mode);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%sMODE:0x%x",
+ (ipc_cli &&
+ (all || some_nt)
+ ? ","
+ : ""),
+ mode);
+ }
+ } else if (strcasecmp_m(name, "mode") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "0x%x", mode);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "0x%x", mode);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_size) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",SIZE:%.0f",
+ (double)size);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",SIZE:%.0f",
+ (double)size);
+ }
+ } else if (strcasecmp_m(name, "size") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%.0f",
+ (double)size);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%.0f",
+ (double)size);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_create_time &&
+ attr_strings.create_time_attr != NULL) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.create_time_attr,
+ (unsigned long) create_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.create_time_attr,
+ (unsigned long) create_time);
+ }
+ } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", (unsigned long) create_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_access_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.access_time_attr,
+ (unsigned long) access_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.access_time_attr,
+ (unsigned long) access_time);
+ }
+ } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", (unsigned long) access_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_write_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.write_time_attr,
+ (unsigned long) write_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.write_time_attr,
+ (unsigned long) write_time);
+ }
+ } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", (unsigned long) write_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_change_time) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx,
+ ",%s:%lu",
+ attr_strings.change_time_attr,
+ (unsigned long) change_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",%s:%lu",
+ attr_strings.change_time_attr,
+ (unsigned long) change_time);
+ }
+ } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%lu", (unsigned long) change_time);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ if (! exclude_dos_inode) {
+ if (all || all_dos) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ ",INODE:%.0f",
+ (double)ino);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ ",INODE:%.0f",
+ (double) ino);
+ }
+ } else if (strcasecmp_m(name, "inode") == 0) {
+ if (determine_size) {
+ p = talloc_asprintf(
+ ctx,
+ "%.0f",
+ (double) ino);
+ if (!p) {
+ errno = ENOMEM;
+ return -1;
+ }
+ n = strlen(p);
+ } else {
+ n = snprintf(buf, bufsize,
+ "%.0f",
+ (double) ino);
+ }
+ }
+
+ if (!determine_size && n > bufsize) {
+ errno = ERANGE;
+ return -1;
+ }
+ buf += n;
+ n_used += n;
+ bufsize -= n;
+ n = 0;
+ }
+
+ /* Restore name pointer to its original value */
+ name -= 16;
+ }
+
+ if (n_used == 0) {
+ errno = ENOATTR;
+ return -1;
+ }
+
+ return n_used;
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int
+cacl_set(SMBCCTX *context,
+ TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ struct cli_state *ipc_cli,
+ struct policy_handle *pol,
+ const char *filename,
+ char *the_acl,
+ int mode,
+ int flags)
+{
+ uint16_t fnum = (uint16_t)-1;
+ int err = 0;
+ struct security_descriptor *sd = NULL, *old;
+ struct security_acl *dacl = NULL;
+ struct dom_sid *owner_sid = NULL;
+ struct dom_sid *group_sid = NULL;
+ uint32_t i, j;
+ size_t sd_size;
+ int ret = 0;
+ char *p;
+ bool numeric = True;
+ char *targetpath = NULL;
+ struct cli_state *targetcli = NULL;
+ struct cli_credentials *creds = NULL;
+ NTSTATUS status;
+
+ /* the_acl will be null for REMOVE_ALL operations */
+ if (the_acl) {
+ numeric = ((p = strchr(the_acl, ':')) != NULL &&
+ p > the_acl &&
+ p[-1] != '+');
+
+ /* if this is to set the entire ACL... */
+ if (*the_acl == '*') {
+ /* ... then increment past the first colon */
+ the_acl = p + 1;
+ }
+
+ sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl);
+ if (!sd) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller
+ that doesn't deref sd */
+
+ if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ creds = context->internal->creds;
+
+ status = cli_resolve_path(ctx, "",
+ creds,
+ cli, filename, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("cacl_set: Could not resolve %s\n", filename));
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ status = cli_ntcreate(
+ targetcli, /* cli */
+ targetpath, /* fname */
+ 0, /* CreatFlags */
+ READ_CONTROL_ACCESS, /* DesiredAccess */
+ 0, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0x0, /* CreateOptions */
+ 0x0, /* SecurityFlags */
+ &fnum, /* pfid */
+ NULL); /* cr */
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ targetpath, nt_errstr(status)));
+ errno = 0;
+ return -1;
+ }
+
+ status = cli_query_secdesc(targetcli, fnum, ctx, &old);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n",
+ targetpath, nt_errstr(status)));
+ errno = 0;
+ return -1;
+ }
+
+ cli_close(targetcli, fnum);
+
+ switch (mode) {
+ case SMBC_XATTR_MODE_REMOVE_ALL:
+ old->dacl->num_aces = 0;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_REMOVE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (security_ace_equal(&sd->dacl->aces[i],
+ &old->dacl->aces[j])) {
+ uint32_t k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->aces[k] =
+ old->dacl->aces[k+1];
+ }
+ old->dacl->num_aces--;
+ found = True;
+ dacl = old->dacl;
+ break;
+ }
+ }
+
+ if (!found) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+ }
+ break;
+
+ case SMBC_XATTR_MODE_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (dom_sid_equal(&sd->dacl->aces[i].trustee,
+ &old->dacl->aces[j].trustee)) {
+ if (!(flags & SMBC_XATTR_FLAG_CREATE)) {
+ err = EEXIST;
+ ret = -1;
+ goto failed;
+ }
+ old->dacl->aces[j] = sd->dacl->aces[i];
+ ret = -1;
+ found = True;
+ }
+ }
+
+ if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) {
+ err = ENOATTR;
+ ret = -1;
+ goto failed;
+ }
+
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->aces[i], ctx);
+ }
+ }
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_SET:
+ old = sd;
+ owner_sid = old->owner_sid;
+ group_sid = old->group_sid;
+ dacl = old->dacl;
+ break;
+
+ case SMBC_XATTR_MODE_CHOWN:
+ owner_sid = sd->owner_sid;
+ break;
+
+ case SMBC_XATTR_MODE_CHGRP:
+ group_sid = sd->group_sid;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+ sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE,
+ owner_sid, group_sid, NULL, dacl, &sd_size);
+
+ status = cli_ntcreate(targetcli, targetpath, 0,
+ WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
+ 0x0, 0x0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("cacl_set failed to open %s: %s\n",
+ targetpath, nt_errstr(status)));
+ errno = 0;
+ return -1;
+ }
+
+ status = cli_set_secdesc(targetcli, fnum, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("ERROR: secdesc set failed: %s\n",
+ nt_errstr(status)));
+ ret = -1;
+ }
+
+ /* Clean up */
+
+failed:
+ cli_close(targetcli, fnum);
+
+ if (err != 0) {
+ errno = err;
+ }
+
+ return ret;
+}
+
+
+int
+SMBC_setxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size,
+ int flags)
+{
+ int ret;
+ int ret2;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ struct DOS_ATTR_DESC *dad = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n",
+ fname, name, (int) size, (const char*)value));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ ipc_srv = SMBC_attr_server(frame, context, server, port, share,
+ &workgroup, &user, &password);
+ if (! ipc_srv) {
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ /*
+ * Are they asking to set the entire set of known attributes?
+ */
+ if (strcasecmp_m(name, "system.*") == 0 ||
+ strcasecmp_m(name, "system.*+") == 0) {
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+7, (const char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (ipc_srv) {
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ } else {
+ ret = 0;
+ }
+
+ /* get a DOS Attribute Descriptor with current attributes */
+ dad = dos_attr_query(context, talloc_tos(), path, srv);
+ if (dad) {
+ bool ok;
+
+ /* Overwrite old with new, using what was provided */
+ dos_attr_parse(context, dad, srv, namevalue);
+
+ /* Set the new DOS attributes */
+ ok = SMBC_setatr(
+ context,
+ srv,
+ path,
+ (struct timespec) {
+ .tv_sec = dad->create_time },
+ (struct timespec) {
+ .tv_sec = dad->access_time },
+ (struct timespec) {
+ .tv_sec = dad->write_time },
+ (struct timespec) {
+ .tv_sec = dad->change_time },
+ dad->mode);
+ if (!ok) {
+ /* cause failure if NT failed too */
+ dad = NULL;
+ }
+ }
+
+ /* we only fail if both NT and DOS sets failed */
+ if (ret < 0 && ! dad) {
+ ret = -1; /* in case dad was null */
+ }
+ else {
+ ret = 0;
+ }
+
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set an access control element or to set
+ * the entire access control list?
+ */
+ if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ ret = -1; /* errno set by SMBC_server() */
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue,
+ (*namevalue == '*'
+ ? SMBC_XATTR_MODE_SET
+ : SMBC_XATTR_MODE_ADD),
+ flags);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the owner?
+ */
+ if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ ret = -1; /* errno set by SMBC_server() */
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue, SMBC_XATTR_MODE_CHOWN, 0);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to set the group?
+ */
+ if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) {
+
+ /* Yup. */
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+19, (const char *) value);
+
+ if (! ipc_srv) {
+ /* errno set by SMBC_server() */
+ ret = -1;
+ }
+ else if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ namevalue, SMBC_XATTR_MODE_CHGRP, 0);
+ }
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+ attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+ }
+
+ /*
+ * Are they asking to set a DOS attribute?
+ */
+ if (strcasecmp_m(name, "system.dos_attr.*") == 0 ||
+ strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
+ (attr_strings.create_time_attr != NULL &&
+ strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
+ strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
+ strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
+ strcasecmp_m(name, attr_strings.change_time_attr) == 0) {
+
+ /* get a DOS Attribute Descriptor with current attributes */
+ dad = dos_attr_query(context, talloc_tos(), path, srv);
+ if (dad) {
+ char *namevalue =
+ talloc_asprintf(talloc_tos(), "%s:%s",
+ name+16, (const char *) value);
+ if (! namevalue) {
+ errno = ENOMEM;
+ ret = -1;
+ } else {
+ /* Overwrite old with provided new params */
+ dos_attr_parse(context, dad, srv, namevalue);
+
+ /* Set the new DOS attributes */
+ ret2 = SMBC_setatr(
+ context,
+ srv,
+ path,
+ (struct timespec) {
+ .tv_sec = dad->create_time },
+ (struct timespec) {
+ .tv_sec = dad->access_time },
+ (struct timespec) {
+ .tv_sec = dad->write_time },
+ (struct timespec) {
+ .tv_sec = dad->change_time },
+ dad->mode);
+
+ /* ret2 has True (success) / False (failure) */
+ if (ret2) {
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ }
+ } else {
+ ret = -1;
+ }
+
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+int
+SMBC_getxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name,
+ const void *value,
+ size_t size)
+{
+ int ret;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ struct {
+ const char * create_time_attr;
+ const char * access_time_attr;
+ const char * write_time_attr;
+ const char * change_time_attr;
+ } attr_strings;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == '\0') {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ ipc_srv = SMBC_attr_server(frame, context, server, port, share,
+ &workgroup, &user, &password);
+ /*
+ * SMBC_attr_server() can cause the original
+ * server to be removed from the cache.
+ * If so we must error out here as the srv
+ * pointer has been freed.
+ */
+ if (smbc_getFunctionGetCachedServer(context)(context,
+ server,
+ share,
+ workgroup,
+ user) != srv) {
+#if defined(ECONNRESET)
+ errno = ECONNRESET;
+#else
+ errno = ETIMEDOUT;
+#endif
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ if (! ipc_srv) {
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ /* Determine whether to use old-style or new-style attribute names */
+ if (context->internal->full_time_names) {
+ /* new-style names */
+ attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME";
+ attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME";
+ } else {
+ /* old-style names */
+ attr_strings.create_time_attr = NULL;
+ attr_strings.access_time_attr = "system.dos_attr.A_TIME";
+ attr_strings.write_time_attr = "system.dos_attr.M_TIME";
+ attr_strings.change_time_attr = "system.dos_attr.C_TIME";
+ }
+
+ /* Are they requesting a supported attribute? */
+ if (strcasecmp_m(name, "system.*") == 0 ||
+ strncasecmp_m(name, "system.*!", 9) == 0 ||
+ strcasecmp_m(name, "system.*+") == 0 ||
+ strncasecmp_m(name, "system.*+!", 10) == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 ||
+ strcasecmp_m(name, "system.dos_attr.*") == 0 ||
+ strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 ||
+ strcasecmp_m(name, "system.dos_attr.mode") == 0 ||
+ strcasecmp_m(name, "system.dos_attr.size") == 0 ||
+ (attr_strings.create_time_attr != NULL &&
+ strcasecmp_m(name, attr_strings.create_time_attr) == 0) ||
+ strcasecmp_m(name, attr_strings.access_time_attr) == 0 ||
+ strcasecmp_m(name, attr_strings.write_time_attr) == 0 ||
+ strcasecmp_m(name, attr_strings.change_time_attr) == 0 ||
+ strcasecmp_m(name, "system.dos_attr.inode") == 0) {
+
+ /* Yup. */
+ const char *filename = name;
+ ret = cacl_get(context, talloc_tos(), srv,
+ ipc_srv == NULL ? NULL : ipc_srv->cli,
+ &ipc_srv->pol, path,
+ filename,
+ discard_const_p(char, value),
+ size);
+ TALLOC_FREE(frame);
+ /*
+ * static function cacl_get returns a value greater than zero
+ * which is needed buffer size needed when size_t is 0.
+ */
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+
+int
+SMBC_removexattr_ctx(SMBCCTX *context,
+ const char *fname,
+ const char *name)
+{
+ int ret;
+ SMBCSRV *srv = NULL;
+ SMBCSRV *ipc_srv = NULL;
+ char *server = NULL;
+ char *share = NULL;
+ char *user = NULL;
+ char *password = NULL;
+ char *workgroup = NULL;
+ char *path = NULL;
+ uint16_t port = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!context || !context->internal->initialized) {
+ errno = EINVAL; /* Best I can think of ... */
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!fname) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name));
+
+ if (SMBC_parse_path(frame,
+ context,
+ fname,
+ &workgroup,
+ &server,
+ &port,
+ &share,
+ &path,
+ &user,
+ &password,
+ NULL)) {
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ if (!user || user[0] == (char)0) {
+ user = talloc_strdup(frame, smbc_getUser(context));
+ if (!user) {
+ errno = ENOMEM;
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ }
+
+ srv = SMBC_server(frame, context, True,
+ server, port, share, &workgroup, &user, &password);
+ if (!srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_server */
+ }
+
+ if (! srv->no_nt_session) {
+ int saved_errno;
+ ipc_srv = SMBC_attr_server(frame, context, server, port, share,
+ &workgroup, &user, &password);
+ saved_errno = errno;
+ /*
+ * SMBC_attr_server() can cause the original
+ * server to be removed from the cache.
+ * If so we must error out here as the srv
+ * pointer has been freed.
+ */
+ if (smbc_getFunctionGetCachedServer(context)(context,
+ server,
+ share,
+ workgroup,
+ user) != srv) {
+#if defined(ECONNRESET)
+ errno = ECONNRESET;
+#else
+ errno = ETIMEDOUT;
+#endif
+ TALLOC_FREE(frame);
+ return -1;
+ }
+ if (! ipc_srv) {
+ errno = saved_errno;
+ srv->no_nt_session = True;
+ }
+ } else {
+ ipc_srv = NULL;
+ }
+
+ if (! ipc_srv) {
+ TALLOC_FREE(frame);
+ return -1; /* errno set by SMBC_attr_server */
+ }
+
+ /* Are they asking to set the entire ACL? */
+ if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) {
+
+ /* Yup. */
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0);
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /*
+ * Are they asking to remove one or more specific security descriptor
+ * attributes?
+ */
+ if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.group") == 0 ||
+ strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 ||
+ strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) {
+
+ /* Yup. */
+ ret = cacl_set(context, talloc_tos(), srv->cli,
+ ipc_srv->cli, &ipc_srv->pol, path,
+ discard_const_p(char, name) + 19,
+ SMBC_XATTR_MODE_REMOVE, 0);
+ TALLOC_FREE(frame);
+ return ret;
+ }
+
+ /* Unsupported attribute name */
+ errno = EINVAL;
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+int
+SMBC_listxattr_ctx(SMBCCTX *context,
+ const char *fname,
+ char *list,
+ size_t size)
+{
+ /*
+ * This isn't quite what listxattr() is supposed to do. This returns
+ * the complete set of attribute names, always, rather than only those
+ * attribute names which actually exist for a file. Hmmm...
+ */
+ size_t retsize;
+ static const char supported_old[] =
+ "system.*\0"
+ "system.*+\0"
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl.*\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ "system.dos_attr.*\0"
+ "system.dos_attr.mode\0"
+ "system.dos_attr.c_time\0"
+ "system.dos_attr.a_time\0"
+ "system.dos_attr.m_time\0"
+ ;
+ static const char supported_new[] =
+ "system.*\0"
+ "system.*+\0"
+ "system.nt_sec_desc.revision\0"
+ "system.nt_sec_desc.owner\0"
+ "system.nt_sec_desc.owner+\0"
+ "system.nt_sec_desc.group\0"
+ "system.nt_sec_desc.group+\0"
+ "system.nt_sec_desc.acl.*\0"
+ "system.nt_sec_desc.acl\0"
+ "system.nt_sec_desc.acl+\0"
+ "system.nt_sec_desc.*\0"
+ "system.nt_sec_desc.*+\0"
+ "system.dos_attr.*\0"
+ "system.dos_attr.mode\0"
+ "system.dos_attr.create_time\0"
+ "system.dos_attr.access_time\0"
+ "system.dos_attr.write_time\0"
+ "system.dos_attr.change_time\0"
+ ;
+ const char * supported;
+
+ if (context->internal->full_time_names) {
+ supported = supported_new;
+ retsize = sizeof(supported_new);
+ } else {
+ supported = supported_old;
+ retsize = sizeof(supported_old);
+ }
+
+ if (size == 0) {
+ return retsize;
+ }
+
+ if (retsize > size) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ /* this can't be strcpy() because there are embedded null characters */
+ memcpy(list, supported, retsize);
+ return retsize;
+}
diff --git a/source3/libsmb/namecache.c b/source3/libsmb/namecache.c
new file mode 100644
index 0000000..eb59b9e
--- /dev/null
+++ b/source3/libsmb/namecache.c
@@ -0,0 +1,480 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NetBIOS name cache module on top of gencache mechanism.
+
+ Copyright (C) Tim Potter 2002
+ Copyright (C) Rafal Szczesniak 2002
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/gencache.h"
+#include "libsmb/namequery.h"
+
+#define IPSTR_LIST_SEP ","
+#define IPSTR_LIST_CHAR ','
+
+/**
+ * Allocate and initialise an ipstr list using samba_sockaddr ip addresses
+ * passed as arguments.
+ *
+ * @param ctx TALLOC_CTX to use
+ * @param ip_list array of ip addresses to place in the list
+ * @param ip_count number of addresses stored in ip_list
+ * @return pointer to allocated ip string
+ **/
+
+static char *ipstr_list_make_sa(TALLOC_CTX *ctx,
+ const struct samba_sockaddr *sa_list,
+ size_t ip_count)
+{
+ char *ipstr_list = NULL;
+ size_t i;
+
+ /* arguments checking */
+ if (sa_list == NULL) {
+ return NULL;
+ }
+
+ /* process ip addresses given as arguments */
+ for (i = 0; i < ip_count; i++) {
+ char addr_buf[INET6_ADDRSTRLEN];
+ char *new_str = NULL;
+
+ print_sockaddr(addr_buf,
+ sizeof(addr_buf),
+ &sa_list[i].u.ss);
+
+ if (sa_list[i].u.ss.ss_family == AF_INET) {
+ /* IPv4 - port no longer used, store 0 */
+ new_str = talloc_asprintf(ctx,
+ "%s:%d",
+ addr_buf,
+ 0);
+ } else {
+ /* IPv6 - port no longer used, store 0 */
+ new_str = talloc_asprintf(ctx,
+ "[%s]:%d",
+ addr_buf,
+ 0);
+ }
+ if (new_str == NULL) {
+ TALLOC_FREE(ipstr_list);
+ return NULL;
+ }
+
+ if (ipstr_list == NULL) {
+ /* First ip address. */
+ ipstr_list = new_str;
+ } else {
+ /*
+ * Append the separator "," and then the new
+ * ip address to the existing list.
+ *
+ * The efficiency here is horrible, but
+ * ip_count should be small enough we can
+ * live with it.
+ */
+ char *tmp = talloc_asprintf(ctx,
+ "%s%s%s",
+ ipstr_list,
+ IPSTR_LIST_SEP,
+ new_str);
+ if (tmp == NULL) {
+ TALLOC_FREE(new_str);
+ TALLOC_FREE(ipstr_list);
+ return NULL;
+ }
+ TALLOC_FREE(new_str);
+ TALLOC_FREE(ipstr_list);
+ ipstr_list = tmp;
+ }
+ }
+
+ return ipstr_list;
+}
+
+/**
+ * Parse given ip string list into array of ip addresses
+ * (as ip_service structures)
+ * e.g. [IPv6]:port,192.168.1.100:389,192.168.1.78, ...
+ *
+ * @param ipstr ip string list to be parsed
+ * @param ip_list pointer to array of ip addresses which is
+ * talloced by this function and must be freed by caller
+ * @return number of successfully parsed addresses
+ **/
+
+static int ipstr_list_parse(TALLOC_CTX *ctx,
+ const char *ipstr_list,
+ struct samba_sockaddr **sa_list_out)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct samba_sockaddr *sa_list = NULL;
+ char *token_str = NULL;
+ size_t count;
+ size_t array_size;
+
+ *sa_list_out = NULL;
+
+ array_size = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1;
+ sa_list = talloc_zero_array(frame,
+ struct samba_sockaddr,
+ array_size);
+ if (sa_list == NULL) {
+ TALLOC_FREE(frame);
+ return 0;
+ }
+
+ count = 0;
+ while (next_token_talloc(frame,
+ &ipstr_list,
+ &token_str,
+ IPSTR_LIST_SEP)) {
+ bool ok;
+ char *s = token_str;
+ char *p = strrchr(token_str, ':');
+ struct sockaddr_storage ss;
+
+ /* Ensure we don't overrun. */
+ if (count >= array_size) {
+ break;
+ }
+
+ if (p) {
+ *p = 0;
+ /* We now ignore the port. */
+ }
+
+ /* convert single token to ip address */
+ if (token_str[0] == '[') {
+ /* IPv6 address. */
+ s++;
+ p = strchr(token_str, ']');
+ if (!p) {
+ continue;
+ }
+ *p = '\0';
+ }
+ ok = interpret_string_addr(&ss, s, AI_NUMERICHOST);
+ if (!ok) {
+ continue;
+ }
+ ok = sockaddr_storage_to_samba_sockaddr(&sa_list[count],
+ &ss);
+ if (!ok) {
+ continue;
+ }
+ count++;
+ }
+ if (count > 0) {
+ *sa_list_out = talloc_move(ctx, &sa_list);
+ }
+ TALLOC_FREE(frame);
+ return count;
+}
+
+#define NBTKEY_FMT "NBT/%s#%02X"
+
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ * type number
+ */
+
+static char *namecache_key(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type)
+{
+ return talloc_asprintf_strupper_m(ctx,
+ NBTKEY_FMT,
+ name,
+ name_type);
+}
+
+/**
+ * Store a name(s) in the name cache - samba_sockaddr version.
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ * ip addresses being stored
+ **/
+
+bool namecache_store(const char *name,
+ int name_type,
+ size_t num_names,
+ struct samba_sockaddr *sa_list)
+{
+ time_t expiry;
+ char *key = NULL;
+ char *value_string = NULL;
+ size_t i;
+ bool ret = false;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (name_type > 255) {
+ /* Don't store non-real name types. */
+ goto out;
+ }
+
+ if ( DEBUGLEVEL >= 5 ) {
+ char *addr = NULL;
+
+ DBG_INFO("storing %zu address%s for %s#%02x: ",
+ num_names, num_names == 1 ? "": "es", name, name_type);
+
+ for (i = 0; i < num_names; i++) {
+ addr = print_canonical_sockaddr(frame,
+ &sa_list[i].u.ss);
+ if (!addr) {
+ continue;
+ }
+ DEBUGADD(5, ("%s%s", addr,
+ (i == (num_names - 1) ? "" : ",")));
+
+ }
+ DEBUGADD(5, ("\n"));
+ }
+
+ key = namecache_key(frame, name, name_type);
+ if (!key) {
+ goto out;
+ }
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+
+ /*
+ * Generate string representation of ip addresses list
+ */
+ value_string = ipstr_list_make_sa(frame, sa_list, num_names);
+ if (value_string == NULL) {
+ goto out;
+ }
+
+ /* set the entry */
+ ret = gencache_set(key, value_string, expiry);
+
+ out:
+
+ TALLOC_FREE(key);
+ TALLOC_FREE(value_string);
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list talloced list of IP addresses if found in the cache,
+ * NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ * false if name isn't found in the cache or has expired
+ **/
+
+bool namecache_fetch(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct samba_sockaddr **sa_list,
+ size_t *num_names)
+{
+ char *key, *value;
+ time_t timeout;
+
+ if (name_type > 255) {
+ return false; /* Don't fetch non-real name types. */
+ }
+
+ *num_names = 0;
+
+ /*
+ * Use gencache interface - lookup the key
+ */
+ key = namecache_key(talloc_tos(), name, name_type);
+ if (!key) {
+ return false;
+ }
+
+ if (!gencache_get(key, talloc_tos(), &value, &timeout)) {
+ DBG_INFO("no entry for %s#%02X found.\n", name, name_type);
+ TALLOC_FREE(key);
+ return false;
+ }
+
+ DBG_INFO("name %s#%02X found.\n", name, name_type);
+
+ /*
+ * Split up the stored value into the list of IP addresses
+ */
+ *num_names = ipstr_list_parse(ctx, value, sa_list);
+
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+
+ return *num_names > 0; /* true only if some ip has been fetched */
+}
+
+/**
+ * Remove a namecache entry. Needed for site support.
+ *
+ **/
+
+bool namecache_delete(const char *name, int name_type)
+{
+ bool ret;
+ char *key;
+
+ if (name_type > 255) {
+ return false; /* Don't fetch non-real name types. */
+ }
+
+ key = namecache_key(talloc_tos(), name, name_type);
+ if (!key) {
+ return false;
+ }
+ ret = gencache_del(key);
+ TALLOC_FREE(key);
+ return ret;
+}
+
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char *key,
+ const char *value,
+ time_t timeout,
+ void *dptr)
+{
+ gencache_del(key);
+ DBG_INFO("Deleting entry %s\n", key);
+}
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return true upon successful deletion or
+ * false in case of an error
+ **/
+
+void namecache_flush(void)
+{
+ /*
+ * iterate through each NBT cache's entry and flush it
+ * by flush_netbios_name function
+ */
+ gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+ DBG_INFO("Namecache flushed\n");
+}
+
+/* Construct a name status record key. */
+
+static char *namecache_status_record_key(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type1,
+ int name_type2,
+ const struct sockaddr_storage *keyip)
+{
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), keyip);
+ return talloc_asprintf_strupper_m(ctx,
+ "NBT/%s#%02X.%02X.%s",
+ name,
+ name_type1,
+ name_type2,
+ addr);
+}
+
+/* Store a name status record. */
+
+bool namecache_status_store(const char *keyname, int keyname_type,
+ int name_type, const struct sockaddr_storage *keyip,
+ const char *srvname)
+{
+ char *key;
+ time_t expiry;
+ bool ret;
+
+ key = namecache_status_record_key(talloc_tos(),
+ keyname,
+ keyname_type,
+ name_type,
+ keyip);
+ if (!key)
+ return false;
+
+ expiry = time(NULL) + lp_name_cache_timeout();
+ ret = gencache_set(key, srvname, expiry);
+
+ if (ret) {
+ DBG_INFO("entry %s -> %s\n", key, srvname);
+ } else {
+ DBG_INFO("entry %s store failed.\n", key);
+ }
+
+ TALLOC_FREE(key);
+ return ret;
+}
+
+/* Fetch a name status record. */
+
+bool namecache_status_fetch(const char *keyname,
+ int keyname_type,
+ int name_type,
+ const struct sockaddr_storage *keyip,
+ char *srvname_out)
+{
+ char *key = NULL;
+ char *value = NULL;
+ time_t timeout;
+
+ key = namecache_status_record_key(talloc_tos(),
+ keyname,
+ keyname_type,
+ name_type,
+ keyip);
+ if (!key)
+ return false;
+
+ if (!gencache_get(key, talloc_tos(), &value, &timeout)) {
+ DBG_INFO("no entry for %s found.\n", key);
+ TALLOC_FREE(key);
+ return false;
+ } else {
+ DBG_INFO("key %s -> %s\n", key, value);
+ }
+
+ strlcpy(srvname_out, value, 16);
+ TALLOC_FREE(key);
+ TALLOC_FREE(value);
+ return true;
+}
diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c
new file mode 100644
index 0000000..e6c0c7d
--- /dev/null
+++ b/source3/libsmb/namequery.c
@@ -0,0 +1,3538 @@
+/*
+ Unix SMB/CIFS implementation.
+ name query routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libsmb/namequery.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "libads/sitename_cache.h"
+#include "../lib/addns/dnsquery.h"
+#include "../lib/addns/dnsquery_srv.h"
+#include "../libcli/netlogon/netlogon.h"
+#include "lib/async_req/async_sock.h"
+#include "lib/tsocket/tsocket.h"
+#include "libsmb/nmblib.h"
+#include "libsmb/unexpected.h"
+#include "../libcli/nbt/libnbt.h"
+#include "libads/kerberos_proto.h"
+#include "lib/gencache.h"
+#include "librpc/gen_ndr/dns.h"
+#include "lib/util/util_net.h"
+#include "lib/util/string_wrappers.h"
+
+/* nmbd.c sets this to True. */
+bool global_in_nmbd = False;
+
+/*
+ * Utility function to convert from a sockaddr_storage
+ * array to a struct samba_sockaddr array.
+ */
+
+static NTSTATUS sockaddr_array_to_samba_sockaddr_array(
+ TALLOC_CTX *ctx,
+ struct samba_sockaddr **sa_out,
+ size_t *count_out,
+ const struct sockaddr_storage *ss_in,
+ size_t count_in)
+{
+ struct samba_sockaddr *sa = NULL;
+ size_t i;
+ size_t count = 0;
+
+ if (count_in == 0) {
+ /*
+ * Zero length arrays are returned as NULL.
+ * in the name resolution code.
+ */
+ *count_out = 0;
+ *sa_out = NULL;
+ return NT_STATUS_OK;
+ }
+ sa = talloc_zero_array(ctx,
+ struct samba_sockaddr,
+ count_in);
+ if (sa == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ count = 0;
+ for (i = 0; i < count_in; i++) {
+ bool ok;
+
+ /* Filter out zero addresses. */
+ if (is_zero_addr(&ss_in[i])) {
+ continue;
+ }
+ ok = sockaddr_storage_to_samba_sockaddr(&sa[count],
+ &ss_in[i]);
+ if (!ok) {
+ continue;
+ }
+ count++;
+ }
+ if (count == 0) {
+ /*
+ * Zero length arrays are returned as NULL.
+ * in the name resolution code.
+ */
+ TALLOC_FREE(sa);
+ }
+ *count_out = count;
+ *sa_out = sa;
+ return NT_STATUS_OK;
+}
+
+/****************************
+ * SERVER AFFINITY ROUTINES *
+ ****************************/
+
+ /* Server affinity is the concept of preferring the last domain
+ controller with whom you had a successful conversation */
+
+/****************************************************************************
+****************************************************************************/
+#define SAFKEY_FMT "SAF/DOMAIN/%s"
+#define SAF_TTL 900
+#define SAFJOINKEY_FMT "SAFJOIN/DOMAIN/%s"
+#define SAFJOIN_TTL 3600
+
+static char *saf_key(TALLOC_CTX *mem_ctx, const char *domain)
+{
+ return talloc_asprintf_strupper_m(mem_ctx, SAFKEY_FMT, domain);
+}
+
+static char *saf_join_key(TALLOC_CTX *mem_ctx, const char *domain)
+{
+ return talloc_asprintf_strupper_m(mem_ctx, SAFJOINKEY_FMT, domain);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+bool saf_store( const char *domain, const char *servername )
+{
+ char *key;
+ time_t expire;
+ bool ret = False;
+
+ if ( !domain || !servername ) {
+ DEBUG(2,("saf_store: "
+ "Refusing to store empty domain or servername!\n"));
+ return False;
+ }
+
+ if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+ DEBUG(0,("saf_store: "
+ "refusing to store 0 length domain or servername!\n"));
+ return False;
+ }
+
+ key = saf_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_key() failed\n"));
+ return false;
+ }
+ expire = time( NULL ) + lp_parm_int(-1, "saf","ttl", SAF_TTL);
+
+ DEBUG(10,("saf_store: domain = [%s], server = [%s], expire = [%u]\n",
+ domain, servername, (unsigned int)expire ));
+
+ ret = gencache_set( key, servername, expire );
+
+ TALLOC_FREE( key );
+
+ return ret;
+}
+
+bool saf_join_store( const char *domain, const char *servername )
+{
+ char *key;
+ time_t expire;
+ bool ret = False;
+
+ if ( !domain || !servername ) {
+ DEBUG(2,("saf_join_store: Refusing to store empty domain or servername!\n"));
+ return False;
+ }
+
+ if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) {
+ DEBUG(0,("saf_join_store: refusing to store 0 length domain or servername!\n"));
+ return False;
+ }
+
+ key = saf_join_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_join_key() failed\n"));
+ return false;
+ }
+ expire = time( NULL ) + lp_parm_int(-1, "saf","join ttl", SAFJOIN_TTL);
+
+ DEBUG(10,("saf_join_store: domain = [%s], server = [%s], expire = [%u]\n",
+ domain, servername, (unsigned int)expire ));
+
+ ret = gencache_set( key, servername, expire );
+
+ TALLOC_FREE( key );
+
+ return ret;
+}
+
+bool saf_delete( const char *domain )
+{
+ char *key;
+ bool ret = False;
+
+ if ( !domain ) {
+ DEBUG(2,("saf_delete: Refusing to delete empty domain\n"));
+ return False;
+ }
+
+ key = saf_join_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_join_key() failed\n"));
+ return false;
+ }
+ ret = gencache_del(key);
+ TALLOC_FREE(key);
+
+ if (ret) {
+ DEBUG(10,("saf_delete[join]: domain = [%s]\n", domain ));
+ }
+
+ key = saf_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_key() failed\n"));
+ return false;
+ }
+ ret = gencache_del(key);
+ TALLOC_FREE(key);
+
+ if (ret) {
+ DEBUG(10,("saf_delete: domain = [%s]\n", domain ));
+ }
+
+ return ret;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+char *saf_fetch(TALLOC_CTX *mem_ctx, const char *domain )
+{
+ char *server = NULL;
+ time_t timeout;
+ bool ret = False;
+ char *key = NULL;
+
+ if ( !domain || strlen(domain) == 0) {
+ DEBUG(2,("saf_fetch: Empty domain name!\n"));
+ return NULL;
+ }
+
+ key = saf_join_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_join_key() failed\n"));
+ return NULL;
+ }
+
+ ret = gencache_get( key, mem_ctx, &server, &timeout );
+
+ TALLOC_FREE( key );
+
+ if ( ret ) {
+ DEBUG(5,("saf_fetch[join]: Returning \"%s\" for \"%s\" domain\n",
+ server, domain ));
+ return server;
+ }
+
+ key = saf_key(talloc_tos(), domain);
+ if (key == NULL) {
+ DEBUG(1, ("saf_key() failed\n"));
+ return NULL;
+ }
+
+ ret = gencache_get( key, mem_ctx, &server, &timeout );
+
+ TALLOC_FREE( key );
+
+ if ( !ret ) {
+ DEBUG(5,("saf_fetch: failed to find server for \"%s\" domain\n",
+ domain ));
+ } else {
+ DEBUG(5,("saf_fetch: Returning \"%s\" for \"%s\" domain\n",
+ server, domain ));
+ }
+
+ return server;
+}
+
+static void set_socket_addr_v4(struct samba_sockaddr *addr)
+{
+ if (!interpret_string_addr(&addr->u.ss, lp_nbt_client_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_sockaddr(&addr->u.ss);
+ /* zero_sockaddr sets family to AF_INET. */
+ addr->sa_socklen = sizeof(struct sockaddr_in);
+ }
+ if (addr->u.ss.ss_family != AF_INET) {
+ zero_sockaddr(&addr->u.ss);
+ /* zero_sockaddr sets family to AF_INET. */
+ addr->sa_socklen = sizeof(struct sockaddr_in);
+ }
+}
+
+static struct in_addr my_socket_addr_v4(void)
+{
+ struct samba_sockaddr my_addr = {0};
+
+ set_socket_addr_v4(&my_addr);
+ return my_addr.u.in.sin_addr;
+}
+
+/****************************************************************************
+ Generate a random trn_id.
+****************************************************************************/
+
+static int generate_trn_id(void)
+{
+ uint16_t id;
+
+ generate_random_buffer((uint8_t *)&id, sizeof(id));
+
+ return id % (unsigned)0x7FFF;
+}
+
+/****************************************************************************
+ Parse a node status response into an array of structures.
+****************************************************************************/
+
+static struct node_status *parse_node_status(TALLOC_CTX *mem_ctx, char *p,
+ size_t *num_names,
+ struct node_status_extra *extra)
+{
+ struct node_status *ret;
+ size_t i;
+ size_t result_count = 0;
+
+ result_count = CVAL(p,0);
+
+ if (result_count == 0)
+ return NULL;
+
+ ret = talloc_array(mem_ctx, struct node_status,result_count);
+ if (!ret)
+ return NULL;
+
+ p++;
+ for (i=0;i< result_count;i++) {
+ strlcpy(ret[i].name,p,16);
+ trim_char(ret[i].name,'\0',' ');
+ ret[i].type = CVAL(p,15);
+ ret[i].flags = p[16];
+ p += 18;
+ DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name,
+ ret[i].type, ret[i].flags));
+ }
+ /*
+ * Also, pick up the MAC address ...
+ */
+ if (extra) {
+ memcpy(&extra->mac_addr, p, 6); /* Fill in the mac addr */
+ }
+ *num_names = result_count;
+ return ret;
+}
+
+struct sock_packet_read_state {
+ struct tevent_context *ev;
+ enum packet_type type;
+ int trn_id;
+
+ struct nb_packet_reader *reader;
+ struct tevent_req *reader_req;
+
+ struct tdgram_context *sock;
+ struct tevent_req *socket_req;
+ uint8_t *buf;
+ struct tsocket_address *addr;
+
+ bool (*validator)(struct packet_struct *p,
+ void *private_data);
+ void *private_data;
+
+ struct packet_struct *packet;
+};
+
+static void sock_packet_read_got_packet(struct tevent_req *subreq);
+static void sock_packet_read_got_socket(struct tevent_req *subreq);
+
+static struct tevent_req *sock_packet_read_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tdgram_context *sock,
+ struct nb_packet_reader *reader,
+ enum packet_type type,
+ int trn_id,
+ bool (*validator)(struct packet_struct *p, void *private_data),
+ void *private_data)
+{
+ struct tevent_req *req;
+ struct sock_packet_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sock_packet_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->reader = reader;
+ state->sock = sock;
+ state->type = type;
+ state->trn_id = trn_id;
+ state->validator = validator;
+ state->private_data = private_data;
+
+ if (reader != NULL) {
+ state->reader_req = nb_packet_read_send(state, ev, reader);
+ if (tevent_req_nomem(state->reader_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->reader_req, sock_packet_read_got_packet, req);
+ }
+
+ state->socket_req = tdgram_recvfrom_send(state, ev, state->sock);
+ if (tevent_req_nomem(state->socket_req, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->socket_req, sock_packet_read_got_socket,
+ req);
+
+ return req;
+}
+
+static void sock_packet_read_got_packet(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct sock_packet_read_state *state = tevent_req_data(
+ req, struct sock_packet_read_state);
+ NTSTATUS status;
+
+ status = nb_packet_read_recv(subreq, state, &state->packet);
+
+ TALLOC_FREE(state->reader_req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (state->socket_req != NULL) {
+ /*
+ * Still waiting for socket
+ */
+ return;
+ }
+ /*
+ * Both socket and packet reader failed
+ */
+ tevent_req_nterror(req, status);
+ return;
+ }
+
+ if ((state->validator != NULL) &&
+ !state->validator(state->packet, state->private_data)) {
+ DEBUG(10, ("validator failed\n"));
+
+ TALLOC_FREE(state->packet);
+
+ state->reader_req = nb_packet_read_send(state, state->ev,
+ state->reader);
+ if (tevent_req_nomem(state->reader_req, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ state->reader_req, sock_packet_read_got_packet, req);
+ return;
+ }
+
+ TALLOC_FREE(state->socket_req);
+ tevent_req_done(req);
+}
+
+static void sock_packet_read_got_socket(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct sock_packet_read_state *state = tevent_req_data(
+ req, struct sock_packet_read_state);
+ struct samba_sockaddr addr = {0};
+ ssize_t ret;
+ ssize_t received;
+ int err;
+ bool ok;
+
+ received = tdgram_recvfrom_recv(subreq, &err, state,
+ &state->buf, &state->addr);
+
+ TALLOC_FREE(state->socket_req);
+
+ if (received == -1) {
+ if (state->reader_req != NULL) {
+ /*
+ * Still waiting for reader
+ */
+ return;
+ }
+ /*
+ * Both socket and reader failed
+ */
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ ok = tsocket_address_is_inet(state->addr, "ipv4");
+ if (!ok) {
+ goto retry;
+ }
+ ret = tsocket_address_bsd_sockaddr(state->addr,
+ &addr.u.sa,
+ sizeof(addr.u.in));
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return;
+ }
+
+ state->packet = parse_packet_talloc(
+ state, (char *)state->buf, received, state->type,
+ addr.u.in.sin_addr, addr.u.in.sin_port);
+ if (state->packet == NULL) {
+ DEBUG(10, ("parse_packet failed\n"));
+ goto retry;
+ }
+ if ((state->trn_id != -1) &&
+ (state->trn_id != packet_trn_id(state->packet))) {
+ DEBUG(10, ("Expected transaction id %d, got %d\n",
+ state->trn_id, packet_trn_id(state->packet)));
+ goto retry;
+ }
+
+ if ((state->validator != NULL) &&
+ !state->validator(state->packet, state->private_data)) {
+ DEBUG(10, ("validator failed\n"));
+ goto retry;
+ }
+
+ tevent_req_done(req);
+ return;
+
+retry:
+ TALLOC_FREE(state->packet);
+ TALLOC_FREE(state->buf);
+ TALLOC_FREE(state->addr);
+
+ state->socket_req = tdgram_recvfrom_send(state, state->ev, state->sock);
+ if (tevent_req_nomem(state->socket_req, req)) {
+ return;
+ }
+ tevent_req_set_callback(state->socket_req, sock_packet_read_got_socket,
+ req);
+}
+
+static NTSTATUS sock_packet_read_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct packet_struct **ppacket)
+{
+ struct sock_packet_read_state *state = tevent_req_data(
+ req, struct sock_packet_read_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *ppacket = talloc_move(mem_ctx, &state->packet);
+ return NT_STATUS_OK;
+}
+
+struct nb_trans_state {
+ struct tevent_context *ev;
+ struct tdgram_context *sock;
+ struct nb_packet_reader *reader;
+
+ struct tsocket_address *src_addr;
+ struct tsocket_address *dst_addr;
+ uint8_t *buf;
+ size_t buflen;
+ enum packet_type type;
+ int trn_id;
+
+ bool (*validator)(struct packet_struct *p,
+ void *private_data);
+ void *private_data;
+
+ struct packet_struct *packet;
+};
+
+static void nb_trans_got_reader(struct tevent_req *subreq);
+static void nb_trans_done(struct tevent_req *subreq);
+static void nb_trans_sent(struct tevent_req *subreq);
+static void nb_trans_send_next(struct tevent_req *subreq);
+
+static struct tevent_req *nb_trans_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct samba_sockaddr *_my_addr,
+ const struct samba_sockaddr *_dst_addr,
+ bool bcast,
+ uint8_t *buf, size_t buflen,
+ enum packet_type type, int trn_id,
+ bool (*validator)(struct packet_struct *p,
+ void *private_data),
+ void *private_data)
+{
+ const struct sockaddr *my_addr = &_my_addr->u.sa;
+ size_t my_addr_len = sizeof(_my_addr->u.in); /*We know it's AF_INET.*/
+ const struct sockaddr *dst_addr = &_dst_addr->u.sa;
+ size_t dst_addr_len = sizeof(_dst_addr->u.in); /*We know it's AF_INET.*/
+ struct tevent_req *req, *subreq;
+ struct nb_trans_state *state;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct nb_trans_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->buf = buf;
+ state->buflen = buflen;
+ state->type = type;
+ state->trn_id = trn_id;
+ state->validator = validator;
+ state->private_data = private_data;
+
+ ret = tsocket_address_bsd_from_sockaddr(state,
+ my_addr, my_addr_len,
+ &state->src_addr);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_bsd_from_sockaddr(state,
+ dst_addr, dst_addr_len,
+ &state->dst_addr);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tdgram_inet_udp_broadcast_socket(state->src_addr, state,
+ &state->sock);
+ if (ret == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = nb_packet_reader_send(state, ev, type, state->trn_id, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nb_trans_got_reader, req);
+ return req;
+}
+
+static void nb_trans_got_reader(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_trans_state *state = tevent_req_data(
+ req, struct nb_trans_state);
+ NTSTATUS status;
+
+ status = nb_packet_reader_recv(subreq, state, &state->reader);
+ TALLOC_FREE(subreq);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("nmbd not around\n"));
+ state->reader = NULL;
+ }
+
+ subreq = sock_packet_read_send(
+ state, state->ev, state->sock,
+ state->reader, state->type, state->trn_id,
+ state->validator, state->private_data);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_trans_done, req);
+
+ subreq = tdgram_sendto_send(state, state->ev,
+ state->sock,
+ state->buf, state->buflen,
+ state->dst_addr);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_trans_sent, req);
+}
+
+static void nb_trans_sent(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_trans_state *state = tevent_req_data(
+ req, struct nb_trans_state);
+ ssize_t sent;
+ int err;
+
+ sent = tdgram_sendto_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (sent == -1) {
+ DEBUG(10, ("sendto failed: %s\n", strerror(err)));
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ subreq = tevent_wakeup_send(state, state->ev,
+ timeval_current_ofs(1, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_trans_send_next, req);
+}
+
+static void nb_trans_send_next(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_trans_state *state = tevent_req_data(
+ req, struct nb_trans_state);
+ bool ret;
+
+ ret = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ subreq = tdgram_sendto_send(state, state->ev,
+ state->sock,
+ state->buf, state->buflen,
+ state->dst_addr);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_trans_sent, req);
+}
+
+static void nb_trans_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_trans_state *state = tevent_req_data(
+ req, struct nb_trans_state);
+ NTSTATUS status;
+
+ status = sock_packet_read_recv(subreq, state, &state->packet);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS nb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct packet_struct **ppacket)
+{
+ struct nb_trans_state *state = tevent_req_data(
+ req, struct nb_trans_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *ppacket = talloc_move(mem_ctx, &state->packet);
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Do a NBT node status query on an open socket and return an array of
+ structures holding the returned names or NULL if the query failed.
+**************************************************************************/
+
+struct node_status_query_state {
+ struct samba_sockaddr my_addr;
+ struct samba_sockaddr addr;
+ uint8_t buf[1024];
+ ssize_t buflen;
+ struct packet_struct *packet;
+};
+
+static bool node_status_query_validator(struct packet_struct *p,
+ void *private_data);
+static void node_status_query_done(struct tevent_req *subreq);
+
+struct tevent_req *node_status_query_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct nmb_name *name,
+ const struct sockaddr_storage *addr)
+{
+ struct tevent_req *req, *subreq;
+ struct node_status_query_state *state;
+ struct packet_struct p;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct node_status_query_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (addr->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
+ return tevent_req_post(req, ev);
+ }
+
+ ok = sockaddr_storage_to_samba_sockaddr(&state->addr, addr);
+ if (!ok) {
+ /* node status must be IPv4 */
+ tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
+ return tevent_req_post(req, ev);
+ }
+ state->addr.u.in.sin_port = htons(NMB_PORT);
+
+ set_socket_addr_v4(&state->my_addr);
+
+ ZERO_STRUCT(p);
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = false;
+ nmb->header.nm_flags.bcast = false;
+ nmb->header.nm_flags.recursion_available = false;
+ nmb->header.nm_flags.recursion_desired = false;
+ nmb->header.nm_flags.trunc = false;
+ nmb->header.nm_flags.authoritative = false;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+ nmb->question.question_name = *name;
+ nmb->question.question_type = 0x21;
+ nmb->question.question_class = 0x1;
+
+ state->buflen = build_packet((char *)state->buf, sizeof(state->buf),
+ &p);
+ if (state->buflen == 0) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ DEBUG(10, ("build_packet failed\n"));
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = nb_trans_send(state,
+ ev,
+ &state->my_addr,
+ &state->addr,
+ false,
+ state->buf,
+ state->buflen,
+ NMB_PACKET,
+ nmb->header.name_trn_id,
+ node_status_query_validator,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ DEBUG(10, ("nb_trans_send failed\n"));
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0))) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, node_status_query_done, req);
+ return req;
+}
+
+static bool node_status_query_validator(struct packet_struct *p,
+ void *private_data)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+ debug_nmb_packet(p);
+
+ if (nmb->header.opcode != 0 ||
+ nmb->header.nm_flags.bcast ||
+ nmb->header.rcode ||
+ !nmb->header.ancount ||
+ nmb->answers->rr_type != 0x21) {
+ /*
+ * XXXX what do we do with this? could be a redirect,
+ * but we'll discard it for the moment
+ */
+ return false;
+ }
+ return true;
+}
+
+static void node_status_query_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct node_status_query_state *state = tevent_req_data(
+ req, struct node_status_query_state);
+ NTSTATUS status;
+
+ status = nb_trans_recv(subreq, state, &state->packet);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS node_status_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct node_status **pnode_status,
+ size_t *pnum_names,
+ struct node_status_extra *extra)
+{
+ struct node_status_query_state *state = tevent_req_data(
+ req, struct node_status_query_state);
+ struct node_status *node_status;
+ size_t num_names = 0;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ node_status = parse_node_status(
+ mem_ctx, &state->packet->packet.nmb.answers->rdata[0],
+ &num_names, extra);
+ if (node_status == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ *pnode_status = node_status;
+ *pnum_names = num_names;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS node_status_query(TALLOC_CTX *mem_ctx, struct nmb_name *name,
+ const struct sockaddr_storage *addr,
+ struct node_status **pnode_status,
+ size_t *pnum_names,
+ struct node_status_extra *extra)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = node_status_query_send(ev, ev, name, addr);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = node_status_query_recv(req, mem_ctx, pnode_status,
+ pnum_names, extra);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool name_status_lmhosts(const struct sockaddr_storage *paddr,
+ int qname_type, fstring pname)
+{
+ FILE *f;
+ char *name;
+ int name_type;
+ struct samba_sockaddr addr_in = {0};
+ struct samba_sockaddr addr = {0};
+ bool ok;
+
+ ok = sockaddr_storage_to_samba_sockaddr(&addr_in, paddr);
+ if (!ok) {
+ return false;
+ }
+ if (addr_in.u.ss.ss_family != AF_INET) {
+ return false;
+ }
+
+ f = startlmhosts(get_dyn_LMHOSTSFILE());
+ if (f == NULL) {
+ return false;
+ }
+
+ while (getlmhostsent(talloc_tos(), f, &name, &name_type, &addr.u.ss)) {
+ if (addr.u.ss.ss_family != AF_INET) {
+ continue;
+ }
+ if (name_type != qname_type) {
+ continue;
+ }
+ if (sockaddr_equal(&addr_in.u.sa, &addr.u.sa)) {
+ fstrcpy(pname, name);
+ endlmhosts(f);
+ return true;
+ }
+ }
+ endlmhosts(f);
+ return false;
+}
+
+/****************************************************************************
+ Find the first type XX name in a node status reply - used for finding
+ a servers name given its IP. Return the matched name in *name.
+**************************************************************************/
+
+bool name_status_find(const char *q_name,
+ int q_type,
+ int type,
+ const struct sockaddr_storage *to_ss,
+ fstring name)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct node_status *addrs = NULL;
+ struct nmb_name nname;
+ size_t count = 0, i;
+ bool result = false;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n",
+ q_name, q_type));
+ return False;
+ }
+
+ print_sockaddr(addr, sizeof(addr), to_ss);
+
+ DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name,
+ q_type, addr));
+
+ /* Check the cache first. */
+
+ if (namecache_status_fetch(q_name, q_type, type, to_ss, name)) {
+ return True;
+ }
+
+ if (to_ss->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ return false;
+ }
+
+ result = name_status_lmhosts(to_ss, type, name);
+ if (result) {
+ DBG_DEBUG("Found name %s in lmhosts\n", name);
+ namecache_status_store(q_name, q_type, type, to_ss, name);
+ return true;
+ }
+
+ /* W2K PDC's seem not to respond to '*'#0. JRA */
+ make_nmb_name(&nname, q_name, q_type);
+ status = node_status_query(talloc_tos(), &nname, to_ss,
+ &addrs, &count, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ for (i=0;i<count;i++) {
+ /* Find first one of the requested type that's not a GROUP. */
+ if (addrs[i].type == type && ! (addrs[i].flags & 0x80))
+ break;
+ }
+ if (i == count)
+ goto done;
+
+ pull_ascii_nstring(name, sizeof(fstring), addrs[i].name);
+
+ /* Store the result in the cache. */
+ /* but don't store an entry for 0x1c names here. Here we have
+ a single host and DOMAIN<0x1c> names should be a list of hosts */
+
+ if ( q_type != 0x1c ) {
+ namecache_status_store(q_name, q_type, type, to_ss, name);
+ }
+
+ result = true;
+
+ done:
+ TALLOC_FREE(addrs);
+
+ DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not "));
+
+ if (result)
+ DEBUGADD(10, (", name %s ip address is %s", name, addr));
+
+ DEBUG(10, ("\n"));
+
+ return result;
+}
+
+/*
+ comparison function used by sort_addr_list
+*/
+
+static int addr_compare(const struct sockaddr_storage *ss1,
+ const struct sockaddr_storage *ss2)
+{
+ int max_bits1=0, max_bits2=0;
+ int num_interfaces = iface_count();
+ int i;
+ struct samba_sockaddr sa1;
+ struct samba_sockaddr sa2;
+ bool ok;
+
+ ok = sockaddr_storage_to_samba_sockaddr(&sa1, ss1);
+ if (!ok) {
+ return 0; /* No change. */
+ }
+
+ ok = sockaddr_storage_to_samba_sockaddr(&sa2, ss2);
+ if (!ok) {
+ return 0; /* No change. */
+ }
+
+ /* Sort IPv4 addresses first. */
+ if (sa1.u.ss.ss_family != sa2.u.ss.ss_family) {
+ if (sa2.u.ss.ss_family == AF_INET) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ /* Here we know both addresses are of the same
+ * family. */
+
+ for (i=0;i<num_interfaces;i++) {
+ struct samba_sockaddr sif = {0};
+ const unsigned char *p_ss1 = NULL;
+ const unsigned char *p_ss2 = NULL;
+ const unsigned char *p_if = NULL;
+ size_t len = 0;
+ int bits1, bits2;
+
+ ok = sockaddr_storage_to_samba_sockaddr(&sif, iface_n_bcast(i));
+ if (!ok) {
+ return 0; /* No change. */
+ }
+ if (sif.u.ss.ss_family != sa1.u.ss.ss_family) {
+ /* Ignore interfaces of the wrong type. */
+ continue;
+ }
+ if (sif.u.ss.ss_family == AF_INET) {
+ p_if = (const unsigned char *)&sif.u.in.sin_addr;
+ p_ss1 = (const unsigned char *)&sa1.u.in.sin_addr;
+ p_ss2 = (const unsigned char *)&sa2.u.in.sin_addr;
+ len = 4;
+ }
+#if defined(HAVE_IPV6)
+ if (sif.u.ss.ss_family == AF_INET6) {
+ p_if = (const unsigned char *)&sif.u.in6.sin6_addr;
+ p_ss1 = (const unsigned char *)&sa1.u.in6.sin6_addr;
+ p_ss2 = (const unsigned char *)&sa2.u.in6.sin6_addr;
+ len = 16;
+ }
+#endif
+ if (!p_ss1 || !p_ss2 || !p_if || len == 0) {
+ continue;
+ }
+ bits1 = matching_len_bits(p_ss1, p_if, len);
+ bits2 = matching_len_bits(p_ss2, p_if, len);
+ max_bits1 = MAX(bits1, max_bits1);
+ max_bits2 = MAX(bits2, max_bits2);
+ }
+
+ /* Bias towards directly reachable IPs */
+ if (iface_local(&sa1.u.sa)) {
+ if (sa1.u.ss.ss_family == AF_INET) {
+ max_bits1 += 32;
+ } else {
+ max_bits1 += 128;
+ }
+ }
+ if (iface_local(&sa2.u.sa)) {
+ if (sa2.u.ss.ss_family == AF_INET) {
+ max_bits2 += 32;
+ } else {
+ max_bits2 += 128;
+ }
+ }
+ return max_bits2 - max_bits1;
+}
+
+/*
+ sort an IP list so that names that are close to one of our interfaces
+ are at the top. This prevents the problem where a WINS server returns an IP
+ that is not reachable from our subnet as the first match
+*/
+
+static void sort_addr_list(struct sockaddr_storage *sslist, size_t count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ TYPESAFE_QSORT(sslist, count, addr_compare);
+}
+
+static int samba_sockaddr_compare(struct samba_sockaddr *sa1,
+ struct samba_sockaddr *sa2)
+{
+ return addr_compare(&sa1->u.ss, &sa2->u.ss);
+}
+
+static void sort_sa_list(struct samba_sockaddr *salist, size_t count)
+{
+ if (count <= 1) {
+ return;
+ }
+
+ TYPESAFE_QSORT(salist, count, samba_sockaddr_compare);
+}
+
+/**********************************************************************
+ Remove any duplicate address/port pairs in the samba_sockaddr array.
+ *********************************************************************/
+
+size_t remove_duplicate_addrs2(struct samba_sockaddr *salist, size_t count )
+{
+ size_t i, j;
+
+ DBG_DEBUG("looking for duplicate address/port pairs\n");
+
+ /* One loop to set duplicates to a zero addr. */
+ for (i=0; i < count; i++) {
+ if (is_zero_addr(&salist[i].u.ss)) {
+ continue;
+ }
+
+ for (j=i+1; j<count; j++) {
+ if (sockaddr_equal(&salist[i].u.sa, &salist[j].u.sa)) {
+ zero_sockaddr(&salist[j].u.ss);
+ }
+ }
+ }
+
+ /* Now remove any addresses set to zero above. */
+ for (i = 0; i < count; i++) {
+ while (i < count &&
+ is_zero_addr(&salist[i].u.ss)) {
+ ARRAY_DEL_ELEMENT(salist, i, count);
+ count--;
+ }
+ }
+
+ return count;
+}
+
+static bool prioritize_ipv4_list(struct samba_sockaddr *salist, size_t count)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct samba_sockaddr *salist_new = talloc_array(frame,
+ struct samba_sockaddr,
+ count);
+ size_t i, j;
+
+ if (salist_new == NULL) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ j = 0;
+
+ /* Copy IPv4 first. */
+ for (i = 0; i < count; i++) {
+ if (salist[i].u.ss.ss_family == AF_INET) {
+ salist_new[j++] = salist[i];
+ }
+ }
+
+ /* Copy IPv6. */
+ for (i = 0; i < count; i++) {
+ if (salist[i].u.ss.ss_family != AF_INET) {
+ salist_new[j++] = salist[i];
+ }
+ }
+
+ memcpy(salist, salist_new, sizeof(struct samba_sockaddr)*count);
+ TALLOC_FREE(frame);
+ return true;
+}
+
+/****************************************************************************
+ Do a netbios name query to find someones IP.
+ Returns an array of IP addresses or NULL if none.
+ *count will be set to the number of addresses returned.
+ *timed_out is set if we failed by timing out
+****************************************************************************/
+
+struct name_query_state {
+ struct samba_sockaddr my_addr;
+ struct samba_sockaddr addr;
+ bool bcast;
+ bool bcast_star_query;
+
+
+ uint8_t buf[1024];
+ ssize_t buflen;
+
+ NTSTATUS validate_error;
+ uint8_t flags;
+
+ struct sockaddr_storage *addrs;
+ size_t num_addrs;
+};
+
+static bool name_query_validator(struct packet_struct *p, void *private_data);
+static void name_query_done(struct tevent_req *subreq);
+
+struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name, int name_type,
+ bool bcast, bool recurse,
+ const struct sockaddr_storage *addr)
+{
+ struct tevent_req *req, *subreq;
+ struct name_query_state *state;
+ struct packet_struct p;
+ struct nmb_packet *nmb = &p.packet.nmb;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct name_query_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->bcast = bcast;
+
+ if (addr->ss_family != AF_INET) {
+ /* Can't do node status to IPv6 */
+ tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
+ return tevent_req_post(req, ev);
+ }
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("name_query(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+ return tevent_req_post(req, ev);
+ }
+
+ ok = sockaddr_storage_to_samba_sockaddr(&state->addr, addr);
+ if (!ok) {
+ /* Node status must be IPv4 */
+ tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
+ return tevent_req_post(req, ev);
+ }
+ state->addr.u.in.sin_port = htons(NMB_PORT);
+
+ set_socket_addr_v4(&state->my_addr);
+
+ ZERO_STRUCT(p);
+ nmb->header.name_trn_id = generate_trn_id();
+ nmb->header.opcode = 0;
+ nmb->header.response = false;
+ nmb->header.nm_flags.bcast = bcast;
+ nmb->header.nm_flags.recursion_available = false;
+ nmb->header.nm_flags.recursion_desired = recurse;
+ nmb->header.nm_flags.trunc = false;
+ nmb->header.nm_flags.authoritative = false;
+ nmb->header.rcode = 0;
+ nmb->header.qdcount = 1;
+ nmb->header.ancount = 0;
+ nmb->header.nscount = 0;
+ nmb->header.arcount = 0;
+
+ if (bcast && (strcmp(name, "*")==0)) {
+ /*
+ * We're doing a broadcast query for all
+ * names in the area. Remember this so
+ * we will wait for all names within
+ * the timeout period.
+ */
+ state->bcast_star_query = true;
+ }
+
+ make_nmb_name(&nmb->question.question_name,name,name_type);
+
+ nmb->question.question_type = 0x20;
+ nmb->question.question_class = 0x1;
+
+ state->buflen = build_packet((char *)state->buf, sizeof(state->buf),
+ &p);
+ if (state->buflen == 0) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ DEBUG(10, ("build_packet failed\n"));
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = nb_trans_send(state,
+ ev,
+ &state->my_addr,
+ &state->addr,
+ bcast,
+ state->buf,
+ state->buflen,
+ NMB_PACKET,
+ nmb->header.name_trn_id,
+ name_query_validator,
+ state);
+ if (tevent_req_nomem(subreq, req)) {
+ DEBUG(10, ("nb_trans_send failed\n"));
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, name_query_done, req);
+ return req;
+}
+
+static bool name_query_validator(struct packet_struct *p, void *private_data)
+{
+ struct name_query_state *state = talloc_get_type_abort(
+ private_data, struct name_query_state);
+ struct nmb_packet *nmb = &p->packet.nmb;
+ struct sockaddr_storage *tmp_addrs;
+ bool got_unique_netbios_name = false;
+ int i;
+
+ debug_nmb_packet(p);
+
+ /*
+ * If we get a Negative Name Query Response from a WINS
+ * server, we should report it and give up.
+ */
+ if( 0 == nmb->header.opcode /* A query response */
+ && !state->bcast /* from a WINS server */
+ && nmb->header.rcode /* Error returned */
+ ) {
+
+ if( DEBUGLVL( 3 ) ) {
+ /* Only executed if DEBUGLEVEL >= 3 */
+ dbgtext( "Negative name query "
+ "response, rcode 0x%02x: ",
+ nmb->header.rcode );
+ switch( nmb->header.rcode ) {
+ case 0x01:
+ dbgtext("Request was invalidly formatted.\n");
+ break;
+ case 0x02:
+ dbgtext("Problem with NBNS, cannot process "
+ "name.\n");
+ break;
+ case 0x03:
+ dbgtext("The name requested does not "
+ "exist.\n");
+ break;
+ case 0x04:
+ dbgtext("Unsupported request error.\n");
+ break;
+ case 0x05:
+ dbgtext("Query refused error.\n");
+ break;
+ default:
+ dbgtext("Unrecognized error code.\n" );
+ break;
+ }
+ }
+
+ /*
+ * We accept this packet as valid, but tell the upper
+ * layers that it's a negative response.
+ */
+ state->validate_error = NT_STATUS_NOT_FOUND;
+ return true;
+ }
+
+ if (nmb->header.opcode != 0 ||
+ nmb->header.nm_flags.bcast ||
+ nmb->header.rcode ||
+ !nmb->header.ancount) {
+ /*
+ * XXXX what do we do with this? Could be a redirect,
+ * but we'll discard it for the moment.
+ */
+ return false;
+ }
+
+ tmp_addrs = talloc_realloc(
+ state, state->addrs, struct sockaddr_storage,
+ state->num_addrs + nmb->answers->rdlength/6);
+ if (tmp_addrs == NULL) {
+ state->validate_error = NT_STATUS_NO_MEMORY;
+ return true;
+ }
+ state->addrs = tmp_addrs;
+
+ DEBUG(2,("Got a positive name query response "
+ "from %s ( ", inet_ntoa(p->ip)));
+
+ for (i=0; i<nmb->answers->rdlength/6; i++) {
+ uint16_t flags;
+ struct in_addr ip;
+ struct sockaddr_storage addr;
+ struct samba_sockaddr sa = {0};
+ bool ok;
+ size_t j;
+
+ flags = RSVAL(&nmb->answers->rdata[i*6], 0);
+ got_unique_netbios_name |= ((flags & 0x8000) == 0);
+
+ putip((char *)&ip,&nmb->answers->rdata[2+i*6]);
+ in_addr_to_sockaddr_storage(&addr, ip);
+
+ ok = sockaddr_storage_to_samba_sockaddr(&sa, &addr);
+ if (!ok) {
+ continue;
+ }
+
+ if (is_zero_addr(&sa.u.ss)) {
+ continue;
+ }
+
+ for (j=0; j<state->num_addrs; j++) {
+ struct samba_sockaddr sa_j = {0};
+
+ ok = sockaddr_storage_to_samba_sockaddr(&sa_j,
+ &state->addrs[j]);
+ if (!ok) {
+ continue;
+ }
+ if (sockaddr_equal(&sa.u.sa, &sa_j.u.sa)) {
+ break;
+ }
+ }
+ if (j < state->num_addrs) {
+ /* Already got it */
+ continue;
+ }
+
+ DEBUGADD(2,("%s ",inet_ntoa(ip)));
+
+ state->addrs[state->num_addrs] = addr;
+ /* wrap check. */
+ if (state->num_addrs + 1 < state->num_addrs) {
+ return false;
+ }
+ state->num_addrs += 1;
+ }
+ DEBUGADD(2,(")\n"));
+
+ /* We add the flags back ... */
+ if (nmb->header.response)
+ state->flags |= NM_FLAGS_RS;
+ if (nmb->header.nm_flags.authoritative)
+ state->flags |= NM_FLAGS_AA;
+ if (nmb->header.nm_flags.trunc)
+ state->flags |= NM_FLAGS_TC;
+ if (nmb->header.nm_flags.recursion_desired)
+ state->flags |= NM_FLAGS_RD;
+ if (nmb->header.nm_flags.recursion_available)
+ state->flags |= NM_FLAGS_RA;
+ if (nmb->header.nm_flags.bcast)
+ state->flags |= NM_FLAGS_B;
+
+ if (state->bcast) {
+ /*
+ * We have to collect all entries coming in from broadcast
+ * queries. If we got a unique name and we are not querying
+ * all names registered within broadcast area (query
+ * for the name '*', so state->bcast_star_query is set),
+ * we're done.
+ */
+ return (got_unique_netbios_name && !state->bcast_star_query);
+ }
+ /*
+ * WINS responses are accepted when they are received
+ */
+ return true;
+}
+
+static void name_query_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct name_query_state *state = tevent_req_data(
+ req, struct name_query_state);
+ NTSTATUS status;
+ struct packet_struct *p = NULL;
+
+ status = nb_trans_recv(subreq, state, &p);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ if (!NT_STATUS_IS_OK(state->validate_error)) {
+ tevent_req_nterror(req, state->validate_error);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS name_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs, size_t *num_addrs,
+ uint8_t *flags)
+{
+ struct name_query_state *state = tevent_req_data(
+ req, struct name_query_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ if (state->bcast &&
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ /*
+ * In the broadcast case we collect replies until the
+ * timeout.
+ */
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+ if (state->num_addrs == 0) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ *addrs = talloc_move(mem_ctx, &state->addrs);
+ sort_addr_list(*addrs, state->num_addrs);
+ *num_addrs = state->num_addrs;
+ if (flags != NULL) {
+ *flags = state->flags;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS name_query(const char *name, int name_type,
+ bool bcast, bool recurse,
+ const struct sockaddr_storage *to_ss,
+ TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs, uint8_t *flags)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ struct timeval timeout;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = name_query_send(ev, ev, name, name_type, bcast, recurse, to_ss);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (bcast) {
+ timeout = timeval_current_ofs(0, 250000);
+ } else {
+ timeout = timeval_current_ofs(2, 0);
+ }
+ if (!tevent_req_set_endtime(req, ev, timeout)) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = name_query_recv(req, mem_ctx, addrs, num_addrs, flags);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct name_queries_state {
+ struct tevent_context *ev;
+ const char *name;
+ int name_type;
+ bool bcast;
+ bool recurse;
+ const struct sockaddr_storage *addrs;
+ size_t num_addrs;
+ int wait_msec;
+ int timeout_msec;
+
+ struct tevent_req **subreqs;
+ size_t num_received;
+ size_t num_sent;
+
+ size_t received_index;
+ struct sockaddr_storage *result_addrs;
+ size_t num_result_addrs;
+ uint8_t flags;
+};
+
+static void name_queries_done(struct tevent_req *subreq);
+static void name_queries_next(struct tevent_req *subreq);
+
+/*
+ * Send a name query to multiple destinations with a wait time in between
+ */
+
+static struct tevent_req *name_queries_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *name, int name_type,
+ bool bcast, bool recurse,
+ const struct sockaddr_storage *addrs,
+ size_t num_addrs, int wait_msec, int timeout_msec)
+{
+ struct tevent_req *req, *subreq;
+ struct name_queries_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct name_queries_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->name = name;
+ state->name_type = name_type;
+ state->bcast = bcast;
+ state->recurse = recurse;
+ state->addrs = addrs;
+ state->num_addrs = num_addrs;
+ state->wait_msec = wait_msec;
+ state->timeout_msec = timeout_msec;
+
+ state->subreqs = talloc_zero_array(
+ state, struct tevent_req *, num_addrs);
+ if (tevent_req_nomem(state->subreqs, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->num_sent = 0;
+
+ subreq = name_query_send(
+ state->subreqs, state->ev, name, name_type, bcast, recurse,
+ &state->addrs[state->num_sent]);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!tevent_req_set_endtime(
+ subreq, state->ev,
+ timeval_current_ofs(0, state->timeout_msec * 1000))) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, name_queries_done, req);
+
+ state->subreqs[state->num_sent] = subreq;
+ state->num_sent += 1;
+
+ if (state->num_sent < state->num_addrs) {
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(0, state->wait_msec * 1000));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, name_queries_next, req);
+ }
+ return req;
+}
+
+static void name_queries_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct name_queries_state *state = tevent_req_data(
+ req, struct name_queries_state);
+ size_t i;
+ NTSTATUS status;
+
+ status = name_query_recv(subreq, state, &state->result_addrs,
+ &state->num_result_addrs, &state->flags);
+
+ for (i=0; i<state->num_sent; i++) {
+ if (state->subreqs[i] == subreq) {
+ break;
+ }
+ }
+ if (i == state->num_sent) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ TALLOC_FREE(state->subreqs[i]);
+
+ /* wrap check. */
+ if (state->num_received + 1 < state->num_received) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+ state->num_received += 1;
+
+ if (!NT_STATUS_IS_OK(status)) {
+
+ if (state->num_received >= state->num_addrs) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ /*
+ * Still outstanding requests, just wait
+ */
+ return;
+ }
+ state->received_index = i;
+ tevent_req_done(req);
+}
+
+static void name_queries_next(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct name_queries_state *state = tevent_req_data(
+ req, struct name_queries_state);
+
+ if (!tevent_wakeup_recv(subreq)) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = name_query_send(
+ state->subreqs, state->ev,
+ state->name, state->name_type, state->bcast, state->recurse,
+ &state->addrs[state->num_sent]);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, name_queries_done, req);
+ if (!tevent_req_set_endtime(
+ subreq, state->ev,
+ timeval_current_ofs(0, state->timeout_msec * 1000))) {
+ return;
+ }
+ state->subreqs[state->num_sent] = subreq;
+ state->num_sent += 1;
+
+ if (state->num_sent < state->num_addrs) {
+ subreq = tevent_wakeup_send(
+ state, state->ev,
+ timeval_current_ofs(0, state->wait_msec * 1000));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, name_queries_next, req);
+ }
+}
+
+static NTSTATUS name_queries_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **result_addrs,
+ size_t *num_result_addrs, uint8_t *flags,
+ size_t *received_index)
+{
+ struct name_queries_state *state = tevent_req_data(
+ req, struct name_queries_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (result_addrs != NULL) {
+ *result_addrs = talloc_move(mem_ctx, &state->result_addrs);
+ }
+ if (num_result_addrs != NULL) {
+ *num_result_addrs = state->num_result_addrs;
+ }
+ if (flags != NULL) {
+ *flags = state->flags;
+ }
+ if (received_index != NULL) {
+ *received_index = state->received_index;
+ }
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Resolve via "bcast" method.
+*********************************************************/
+
+struct name_resolve_bcast_state {
+ struct sockaddr_storage *addrs;
+ size_t num_addrs;
+};
+
+static void name_resolve_bcast_done(struct tevent_req *subreq);
+
+struct tevent_req *name_resolve_bcast_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name,
+ int name_type)
+{
+ struct tevent_req *req, *subreq;
+ struct name_resolve_bcast_state *state;
+ struct sockaddr_storage *bcast_addrs;
+ size_t i, num_addrs, num_bcast_addrs;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct name_resolve_bcast_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (lp_disable_netbios()) {
+ DEBUG(5, ("name_resolve_bcast(%s#%02x): netbios is disabled\n",
+ name, name_type));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * "bcast" means do a broadcast lookup on all the local interfaces.
+ */
+
+ DEBUG(3, ("name_resolve_bcast: Attempting broadcast lookup "
+ "for name %s<0x%x>\n", name, name_type));
+
+ num_addrs = iface_count();
+ bcast_addrs = talloc_array(state, struct sockaddr_storage, num_addrs);
+ if (tevent_req_nomem(bcast_addrs, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * Lookup the name on all the interfaces, return on
+ * the first successful match.
+ */
+ num_bcast_addrs = 0;
+
+ for (i=0; i<num_addrs; i++) {
+ const struct sockaddr_storage *pss = iface_n_bcast(i);
+
+ if (pss->ss_family != AF_INET) {
+ continue;
+ }
+ bcast_addrs[num_bcast_addrs] = *pss;
+ num_bcast_addrs += 1;
+ }
+
+ subreq = name_queries_send(state, ev, name, name_type, true, true,
+ bcast_addrs, num_bcast_addrs, 0, 250);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, name_resolve_bcast_done, req);
+ return req;
+}
+
+static void name_resolve_bcast_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct name_resolve_bcast_state *state = tevent_req_data(
+ req, struct name_resolve_bcast_state);
+ NTSTATUS status;
+
+ status = name_queries_recv(subreq, state,
+ &state->addrs, &state->num_addrs,
+ NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS name_resolve_bcast_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs)
+{
+ struct name_resolve_bcast_state *state = tevent_req_data(
+ req, struct name_resolve_bcast_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *addrs = talloc_move(mem_ctx, &state->addrs);
+ *num_addrs = state->num_addrs;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS name_resolve_bcast(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_iplist,
+ size_t *return_count)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = name_resolve_bcast_send(frame, ev, name, name_type);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = name_resolve_bcast_recv(req, mem_ctx, return_iplist,
+ return_count);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct query_wins_list_state {
+ struct tevent_context *ev;
+ const char *name;
+ uint8_t name_type;
+ struct in_addr *servers;
+ size_t num_servers;
+ struct sockaddr_storage server;
+ size_t num_sent;
+
+ struct sockaddr_storage *addrs;
+ size_t num_addrs;
+ uint8_t flags;
+};
+
+static void query_wins_list_done(struct tevent_req *subreq);
+
+/*
+ * Query a list of (replicating) wins servers in sequence, call them
+ * dead if they don't reply
+ */
+
+static struct tevent_req *query_wins_list_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct in_addr src_ip, const char *name, uint8_t name_type,
+ struct in_addr *servers, size_t num_servers)
+{
+ struct tevent_req *req, *subreq;
+ struct query_wins_list_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct query_wins_list_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->name = name;
+ state->name_type = name_type;
+ state->servers = servers;
+ state->num_servers = num_servers;
+
+ if (state->num_servers == 0) {
+ tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+ return tevent_req_post(req, ev);
+ }
+
+ in_addr_to_sockaddr_storage(
+ &state->server, state->servers[state->num_sent]);
+
+ subreq = name_query_send(state, state->ev,
+ state->name, state->name_type,
+ false, true, &state->server);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /* wrap check */
+ if (state->num_sent + 1 < state->num_sent) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->num_sent += 1;
+ if (!tevent_req_set_endtime(subreq, state->ev,
+ timeval_current_ofs(2, 0))) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, query_wins_list_done, req);
+ return req;
+}
+
+static void query_wins_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct query_wins_list_state *state = tevent_req_data(
+ req, struct query_wins_list_state);
+ NTSTATUS status;
+
+ status = name_query_recv(subreq, state,
+ &state->addrs, &state->num_addrs,
+ &state->flags);
+ TALLOC_FREE(subreq);
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return;
+ }
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ wins_srv_died(state->servers[state->num_sent-1],
+ my_socket_addr_v4());
+
+ if (state->num_sent == state->num_servers) {
+ tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+ return;
+ }
+
+ in_addr_to_sockaddr_storage(
+ &state->server, state->servers[state->num_sent]);
+
+ subreq = name_query_send(state, state->ev,
+ state->name, state->name_type,
+ false, true, &state->server);
+ state->num_sent += 1;
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ if (!tevent_req_set_endtime(subreq, state->ev,
+ timeval_current_ofs(2, 0))) {
+ return;
+ }
+ tevent_req_set_callback(subreq, query_wins_list_done, req);
+}
+
+static NTSTATUS query_wins_list_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs,
+ uint8_t *flags)
+{
+ struct query_wins_list_state *state = tevent_req_data(
+ req, struct query_wins_list_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (addrs != NULL) {
+ *addrs = talloc_move(mem_ctx, &state->addrs);
+ }
+ if (num_addrs != NULL) {
+ *num_addrs = state->num_addrs;
+ }
+ if (flags != NULL) {
+ *flags = state->flags;
+ }
+ return NT_STATUS_OK;
+}
+
+struct resolve_wins_state {
+ size_t num_sent;
+ size_t num_received;
+
+ struct sockaddr_storage *addrs;
+ size_t num_addrs;
+ uint8_t flags;
+};
+
+static void resolve_wins_done(struct tevent_req *subreq);
+
+struct tevent_req *resolve_wins_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name,
+ int name_type)
+{
+ struct tevent_req *req, *subreq;
+ struct resolve_wins_state *state;
+ char **wins_tags = NULL;
+ struct sockaddr_storage src_ss;
+ struct samba_sockaddr src_sa = {0};
+ struct in_addr src_ip;
+ size_t i, num_wins_tags;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct resolve_wins_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (wins_srv_count() < 1) {
+ DEBUG(3,("resolve_wins: WINS server resolution selected "
+ "and no WINS servers listed.\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ /* the address we will be sending from */
+ if (!interpret_string_addr(&src_ss, lp_nbt_client_socket_address(),
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ zero_sockaddr(&src_ss);
+ }
+
+ ok = sockaddr_storage_to_samba_sockaddr(&src_sa, &src_ss);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ if (src_sa.u.ss.ss_family != AF_INET) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &src_sa.u.ss);
+ DEBUG(3,("resolve_wins: cannot receive WINS replies "
+ "on IPv6 address %s\n",
+ addr));
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ src_ip = src_sa.u.in.sin_addr;
+
+ wins_tags = wins_srv_tags();
+ if (wins_tags == NULL) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+
+ num_wins_tags = 0;
+ while (wins_tags[num_wins_tags] != NULL) {
+ /* wrap check. */
+ if (num_wins_tags + 1 < num_wins_tags) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ goto fail;
+ }
+ num_wins_tags += 1;
+ }
+
+ for (i=0; i<num_wins_tags; i++) {
+ size_t num_servers, num_alive;
+ struct in_addr *servers, *alive;
+ size_t j;
+
+ if (!wins_server_tag_ips(wins_tags[i], talloc_tos(),
+ &servers, &num_servers)) {
+ DEBUG(10, ("wins_server_tag_ips failed for tag %s\n",
+ wins_tags[i]));
+ continue;
+ }
+
+ alive = talloc_array(state, struct in_addr, num_servers);
+ if (tevent_req_nomem(alive, req)) {
+ goto fail;
+ }
+
+ num_alive = 0;
+ for (j=0; j<num_servers; j++) {
+ struct in_addr wins_ip = servers[j];
+
+ if (global_in_nmbd && ismyip_v4(wins_ip)) {
+ /* yikes! we'll loop forever */
+ continue;
+ }
+ /* skip any that have been unresponsive lately */
+ if (wins_srv_is_dead(wins_ip, src_ip)) {
+ continue;
+ }
+ DEBUG(3, ("resolve_wins: using WINS server %s "
+ "and tag '%s'\n",
+ inet_ntoa(wins_ip), wins_tags[i]));
+ alive[num_alive] = wins_ip;
+ num_alive += 1;
+ }
+ TALLOC_FREE(servers);
+
+ if (num_alive == 0) {
+ continue;
+ }
+
+ subreq = query_wins_list_send(
+ state, ev, src_ip, name, name_type,
+ alive, num_alive);
+ if (tevent_req_nomem(subreq, req)) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, resolve_wins_done, req);
+ state->num_sent += 1;
+ }
+
+ if (state->num_sent == 0) {
+ tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
+ goto fail;
+ }
+
+ wins_srv_tags_free(wins_tags);
+ return req;
+fail:
+ wins_srv_tags_free(wins_tags);
+ return tevent_req_post(req, ev);
+}
+
+static void resolve_wins_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct resolve_wins_state *state = tevent_req_data(
+ req, struct resolve_wins_state);
+ NTSTATUS status;
+
+ status = query_wins_list_recv(subreq, state, &state->addrs,
+ &state->num_addrs, &state->flags);
+ if (NT_STATUS_IS_OK(status)) {
+ tevent_req_done(req);
+ return;
+ }
+
+ /* wrap check. */
+ if (state->num_received + 1 < state->num_received) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ state->num_received += 1;
+
+ if (state->num_received < state->num_sent) {
+ /*
+ * Wait for the others
+ */
+ return;
+ }
+ tevent_req_nterror(req, status);
+}
+
+NTSTATUS resolve_wins_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs, uint8_t *flags)
+{
+ struct resolve_wins_state *state = tevent_req_data(
+ req, struct resolve_wins_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (addrs != NULL) {
+ *addrs = talloc_move(mem_ctx, &state->addrs);
+ }
+ if (num_addrs != NULL) {
+ *num_addrs = state->num_addrs;
+ }
+ if (flags != NULL) {
+ *flags = state->flags;
+ }
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Resolve via "wins" method.
+*********************************************************/
+
+NTSTATUS resolve_wins(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_iplist,
+ size_t *return_count)
+{
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = resolve_wins_send(ev, ev, name, name_type);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = resolve_wins_recv(req, mem_ctx, return_iplist, return_count,
+ NULL);
+fail:
+ TALLOC_FREE(ev);
+ return status;
+}
+
+
+/********************************************************
+ Resolve via "hosts" method.
+*********************************************************/
+
+static NTSTATUS resolve_hosts(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_iplist,
+ size_t *return_count)
+{
+ /*
+ * "host" means do a localhost, or dns lookup.
+ */
+ struct addrinfo hints;
+ struct addrinfo *ailist = NULL;
+ struct addrinfo *res = NULL;
+ int ret = -1;
+ size_t i = 0;
+ size_t ret_count = 0;
+ struct sockaddr_storage *iplist = NULL;
+
+ if ( name_type != 0x20 && name_type != 0x0) {
+ DEBUG(5, ("resolve_hosts: not appropriate "
+ "for name type <0x%x>\n",
+ name_type));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n",
+ name, name_type));
+
+ ZERO_STRUCT(hints);
+ /* By default make sure it supports TCP. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+#if !defined(HAVE_IPV6)
+ /* Unless we have IPv6, we really only want IPv4 addresses back. */
+ hints.ai_family = AF_INET;
+#endif
+
+ ret = getaddrinfo(name,
+ NULL,
+ &hints,
+ &ailist);
+ if (ret) {
+ DEBUG(3,("resolve_hosts: getaddrinfo failed for name %s [%s]\n",
+ name,
+ gai_strerror(ret) ));
+ }
+
+ for (res = ailist; res; res = res->ai_next) {
+ struct sockaddr_storage ss = {0};
+ struct sockaddr_storage *tmp = NULL;
+
+ if ((res->ai_addr == NULL) ||
+ (res->ai_addrlen == 0) ||
+ (res->ai_addrlen > sizeof(ss))) {
+ continue;
+ }
+
+ memcpy(&ss, res->ai_addr, res->ai_addrlen);
+
+ if (is_zero_addr(&ss)) {
+ continue;
+ }
+
+ /* wrap check. */
+ if (ret_count + 1 < ret_count) {
+ freeaddrinfo(ailist);
+ TALLOC_FREE(iplist);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ret_count += 1;
+
+ tmp = talloc_realloc(
+ mem_ctx, iplist, struct sockaddr_storage,
+ ret_count);
+ if (tmp == NULL) {
+ DEBUG(3,("resolve_hosts: malloc fail !\n"));
+ freeaddrinfo(ailist);
+ TALLOC_FREE(iplist);
+ return NT_STATUS_NO_MEMORY;
+ }
+ iplist = tmp;
+ iplist[i] = ss;
+ i++;
+ }
+ if (ailist) {
+ freeaddrinfo(ailist);
+ }
+ if (ret_count == 0) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ *return_count = ret_count;
+ *return_iplist = iplist;
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Resolve via "ADS" method.
+*********************************************************/
+
+/* Special name type used to cause a _kerberos DNS lookup. */
+#define KDC_NAME_TYPE 0xDCDC
+
+static NTSTATUS resolve_ads(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ const char *sitename,
+ struct sockaddr_storage **return_addrs,
+ size_t *return_count)
+{
+ size_t i;
+ NTSTATUS status;
+ struct dns_rr_srv *dcs = NULL;
+ size_t numdcs = 0;
+ size_t num_srv_addrs = 0;
+ struct sockaddr_storage *srv_addrs = NULL;
+ char *query = NULL;
+
+ if ((name_type != 0x1c) && (name_type != KDC_NAME_TYPE) &&
+ (name_type != 0x1b)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = NT_STATUS_OK;
+
+ switch (name_type) {
+ case 0x1b:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "PDC for %s using DNS\n", name));
+ query = ads_dns_query_string_pdc(ctx, name);
+ break;
+
+ case 0x1c:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "DCs for %s using DNS\n", name));
+ query = ads_dns_query_string_dcs(ctx, name);
+ break;
+ case KDC_NAME_TYPE:
+ DEBUG(5,("resolve_ads: Attempting to resolve "
+ "KDCs for %s using DNS\n", name));
+ query = ads_dns_query_string_kdcs(ctx, name);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (query == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DBG_DEBUG("SRV query for %s\n", query);
+
+ status = ads_dns_query_srv(
+ ctx,
+ lp_get_async_dns_timeout(),
+ sitename,
+ query,
+ &dcs,
+ &numdcs);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (numdcs == 0) {
+ *return_addrs = NULL;
+ *return_count = 0;
+ TALLOC_FREE(dcs);
+ return NT_STATUS_OK;
+ }
+
+ /* First count the sizes of each array. */
+ for(i = 0; i < numdcs; i++) {
+ if (dcs[i].ss_s == NULL) {
+ /*
+ * Nothing received or timeout in A/AAAA reqs
+ */
+ continue;
+ }
+
+ if (num_srv_addrs + dcs[i].num_ips < num_srv_addrs) {
+ /* Wrap check. */
+ TALLOC_FREE(dcs);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ /* Add in the number of addresses we got. */
+ num_srv_addrs += dcs[i].num_ips;
+ }
+
+ /* Allocate the list of IP addresses we already have. */
+ srv_addrs = talloc_zero_array(ctx,
+ struct sockaddr_storage,
+ num_srv_addrs);
+ if (srv_addrs == NULL) {
+ TALLOC_FREE(dcs);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ num_srv_addrs = 0;
+ for(i = 0; i < numdcs; i++) {
+ /* Copy all the IP addresses from the SRV response */
+ size_t j;
+ for (j = 0; j < dcs[i].num_ips; j++) {
+ char addr[INET6_ADDRSTRLEN];
+
+ srv_addrs[num_srv_addrs] = dcs[i].ss_s[j];
+ if (is_zero_addr(&srv_addrs[num_srv_addrs])) {
+ continue;
+ }
+
+ DBG_DEBUG("SRV lookup %s got IP[%zu] %s\n",
+ name,
+ j,
+ print_sockaddr(addr,
+ sizeof(addr),
+ &srv_addrs[num_srv_addrs]));
+
+ num_srv_addrs++;
+ }
+ }
+
+ TALLOC_FREE(dcs);
+
+ *return_addrs = srv_addrs;
+ *return_count = num_srv_addrs;
+ return NT_STATUS_OK;
+}
+
+static const char **filter_out_nbt_lookup(TALLOC_CTX *mem_ctx,
+ const char **resolve_order)
+{
+ size_t i, len, result_idx;
+ const char **result;
+
+ len = 0;
+ while (resolve_order[len] != NULL) {
+ len += 1;
+ }
+
+ result = talloc_array(mem_ctx, const char *, len+1);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result_idx = 0;
+
+ for (i=0; i<len; i++) {
+ const char *tok = resolve_order[i];
+
+ if (strequal(tok, "lmhosts") || strequal(tok, "wins") ||
+ strequal(tok, "bcast")) {
+ continue;
+ }
+ result[result_idx++] = tok;
+ }
+ result[result_idx] = NULL;
+
+ return result;
+}
+
+/*******************************************************************
+ Samba interface to resolve a name into an IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+
+ Added support for ip addr/port to support ADS ldap servers.
+ the only place we currently care about the port is in the
+ resolve_hosts() when looking up DC's via SRV RR entries in DNS
+**********************************************************************/
+
+NTSTATUS internal_resolve_name(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ const char *sitename,
+ struct samba_sockaddr **return_salist,
+ size_t *return_count,
+ const char **resolve_order)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ size_t i;
+ size_t nc_count = 0;
+ size_t ret_count = 0;
+ bool ok;
+ struct sockaddr_storage *ss_list = NULL;
+ struct samba_sockaddr *sa_list = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ DBG_DEBUG("looking up %s#%x (sitename %s)\n",
+ name, name_type, sitename ? sitename : "(null)");
+
+ if (is_ipaddress(name)) {
+ struct sockaddr_storage ss;
+
+ /* if it's in the form of an IP address then get the lib to interpret it */
+ ok = interpret_string_addr(&ss, name, AI_NUMERICHOST);
+ if (!ok) {
+ DBG_WARNING("interpret_string_addr failed on %s\n",
+ name);
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (is_zero_addr(&ss)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = sockaddr_array_to_samba_sockaddr_array(frame,
+ &sa_list,
+ &ret_count,
+ &ss,
+ 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ *return_salist = talloc_move(ctx, &sa_list);
+ *return_count = 1;
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ /* Check name cache */
+
+ ok = namecache_fetch(frame,
+ name,
+ name_type,
+ &sa_list,
+ &nc_count);
+ if (ok) {
+ /*
+ * remove_duplicate_addrs2() has the
+ * side effect of removing zero addresses,
+ * so use it here.
+ */
+ nc_count = remove_duplicate_addrs2(sa_list, nc_count);
+ if (nc_count == 0) {
+ TALLOC_FREE(sa_list);
+ TALLOC_FREE(frame);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ *return_count = nc_count;
+ *return_salist = talloc_move(ctx, &sa_list);
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ /* set the name resolution order */
+
+ if (resolve_order && strcmp(resolve_order[0], "NULL") == 0) {
+ DBG_DEBUG("all lookups disabled\n");
+ TALLOC_FREE(frame);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!resolve_order || !resolve_order[0]) {
+ static const char *host_order[] = { "host", NULL };
+ resolve_order = host_order;
+ }
+
+ if ((strlen(name) > MAX_NETBIOSNAME_LEN - 1) ||
+ (strchr(name, '.') != NULL)) {
+ /*
+ * Don't do NBT lookup, the name would not fit anyway
+ */
+ resolve_order = filter_out_nbt_lookup(frame, resolve_order);
+ if (resolve_order == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* iterate through the name resolution backends */
+
+ for (i=0; resolve_order[i]; i++) {
+ const char *tok = resolve_order[i];
+
+ if ((strequal(tok, "host") || strequal(tok, "hosts"))) {
+ status = resolve_hosts(talloc_tos(),
+ name,
+ name_type,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ goto done;
+ }
+
+ if (strequal(tok, "kdc")) {
+ /* deal with KDC_NAME_TYPE names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(talloc_tos(),
+ name,
+ KDC_NAME_TYPE,
+ sitename,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ /* Ensure we don't namecache
+ * this with the KDC port. */
+ name_type = KDC_NAME_TYPE;
+ goto done;
+ }
+
+ if (strequal(tok, "ads")) {
+ /* deal with 0x1c and 0x1b names here.
+ * This will result in a SRV record lookup */
+ status = resolve_ads(talloc_tos(),
+ name,
+ name_type,
+ sitename,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ goto done;
+ }
+
+ if (strequal(tok, "lmhosts")) {
+ status = resolve_lmhosts_file_as_sockaddr(
+ talloc_tos(),
+ get_dyn_LMHOSTSFILE(),
+ name,
+ name_type,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ goto done;
+ }
+
+ if (strequal(tok, "wins")) {
+ /* don't resolve 1D via WINS */
+ if (name_type == 0x1D) {
+ continue;
+ }
+ status = resolve_wins(talloc_tos(),
+ name,
+ name_type,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ goto done;
+ }
+
+ if (strequal(tok, "bcast")) {
+ status = name_resolve_bcast(
+ talloc_tos(),
+ name,
+ name_type,
+ &ss_list,
+ &ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ goto done;
+ }
+
+ DBG_ERR("unknown name switch type %s\n", tok);
+ }
+
+ /* All of the resolve_* functions above have returned false. */
+
+ TALLOC_FREE(frame);
+ *return_count = 0;
+
+ return status;
+
+ done:
+
+ status = sockaddr_array_to_samba_sockaddr_array(frame,
+ &sa_list,
+ &ret_count,
+ ss_list,
+ ret_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Remove duplicate entries. Some queries, notably #1c (domain
+ controllers) return the PDC in iplist[0] and then all domain
+ controllers including the PDC in iplist[1..n]. Iterating over
+ the iplist when the PDC is down will cause two sets of timeouts. */
+
+ ret_count = remove_duplicate_addrs2(sa_list, ret_count);
+
+ /* Save in name cache */
+ if ( DEBUGLEVEL >= 100 ) {
+ for (i = 0; i < ret_count && DEBUGLEVEL == 100; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &sa_list[i].u.ss);
+ DEBUG(100, ("Storing name %s of type %d (%s:0)\n",
+ name,
+ name_type,
+ addr));
+ }
+ }
+
+ if (ret_count) {
+ namecache_store(name,
+ name_type,
+ ret_count,
+ sa_list);
+ }
+
+ /* Display some debugging info */
+
+ if ( DEBUGLEVEL >= 10 ) {
+ DBG_DEBUG("returning %zu addresses: ",
+ ret_count);
+
+ for (i = 0; i < ret_count; i++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr),
+ &sa_list[i].u.ss);
+ DEBUGADD(10, ("%s ", addr));
+ }
+ DEBUG(10, ("\n"));
+ }
+
+ *return_count = ret_count;
+ *return_salist = talloc_move(ctx, &sa_list);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/********************************************************
+ Internal interface to resolve a name into one IP address.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+bool resolve_name(const char *name,
+ struct sockaddr_storage *return_ss,
+ int name_type,
+ bool prefer_ipv4)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ char *sitename = NULL;
+ size_t count = 0;
+ NTSTATUS status;
+ TALLOC_CTX *frame = NULL;
+
+ if (is_ipaddress(name)) {
+ return interpret_string_addr(return_ss, name, AI_NUMERICHOST);
+ }
+
+ frame = talloc_stackframe();
+
+ sitename = sitename_fetch(frame, lp_realm()); /* wild guess */
+
+ status = internal_resolve_name(frame,
+ name,
+ name_type,
+ sitename,
+ &sa_list,
+ &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ size_t i;
+
+ if (prefer_ipv4) {
+ for (i=0; i<count; i++) {
+ if (!is_broadcast_addr(&sa_list[i].u.sa) &&
+ (sa_list[i].u.ss.ss_family == AF_INET)) {
+ *return_ss = sa_list[i].u.ss;
+ TALLOC_FREE(sa_list);
+ TALLOC_FREE(frame);
+ return True;
+ }
+ }
+ }
+
+ /* only return valid addresses for TCP connections */
+ for (i=0; i<count; i++) {
+ if (!is_broadcast_addr(&sa_list[i].u.sa)) {
+ *return_ss = sa_list[i].u.ss;
+ TALLOC_FREE(sa_list);
+ TALLOC_FREE(frame);
+ return True;
+ }
+ }
+ }
+
+ TALLOC_FREE(sa_list);
+ TALLOC_FREE(frame);
+ return False;
+}
+
+/********************************************************
+ Internal interface to resolve a name into a list of IP addresses.
+ Use this function if the string is either an IP address, DNS
+ or host name or NetBIOS name. This uses the name switch in the
+ smb.conf to determine the order of name resolution.
+*********************************************************/
+
+NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_ss_arr,
+ unsigned int *p_num_entries)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ char *sitename = NULL;
+ size_t count = 0;
+ size_t i;
+ unsigned int num_entries = 0;
+ struct sockaddr_storage *result_arr = NULL;
+ NTSTATUS status;
+
+ if (is_ipaddress(name)) {
+ result_arr = talloc(ctx, struct sockaddr_storage);
+ if (result_arr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (!interpret_string_addr(result_arr, name, AI_NUMERICHOST)) {
+ TALLOC_FREE(result_arr);
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+ *p_num_entries = 1;
+ *return_ss_arr = result_arr;
+ return NT_STATUS_OK;
+ }
+
+ sitename = sitename_fetch(ctx, lp_realm()); /* wild guess */
+
+ status = internal_resolve_name(ctx,
+ name,
+ name_type,
+ sitename,
+ &sa_list,
+ &count,
+ lp_name_resolve_order());
+ TALLOC_FREE(sitename);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only return valid addresses for TCP connections */
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&sa_list[i].u.ss) &&
+ !is_broadcast_addr(&sa_list[i].u.sa)) {
+ num_entries++;
+ }
+ }
+ if (num_entries == 0) {
+ status = NT_STATUS_BAD_NETWORK_NAME;
+ goto done;
+ }
+
+ result_arr = talloc_array(ctx,
+ struct sockaddr_storage,
+ num_entries);
+ if (result_arr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0, num_entries = 0; i<count; i++) {
+ if (!is_zero_addr(&sa_list[i].u.ss) &&
+ !is_broadcast_addr(&sa_list[i].u.sa)) {
+ result_arr[num_entries++] = sa_list[i].u.ss;
+ }
+ }
+
+ if (num_entries == 0) {
+ TALLOC_FREE(result_arr);
+ status = NT_STATUS_BAD_NETWORK_NAME;
+ goto done;
+ }
+
+ status = NT_STATUS_OK;
+ *p_num_entries = num_entries;
+ *return_ss_arr = result_arr;
+done:
+ TALLOC_FREE(sa_list);
+ return status;
+}
+
+/********************************************************
+ Find the IP address of the master browser or DMB for a workgroup.
+*********************************************************/
+
+bool find_master_ip(const char *group, struct sockaddr_storage *master_ss)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ size_t count = 0;
+ NTSTATUS status;
+
+ if (lp_disable_netbios()) {
+ DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group));
+ return false;
+ }
+
+ status = internal_resolve_name(talloc_tos(),
+ group,
+ 0x1D,
+ NULL,
+ &sa_list,
+ &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = sa_list[0].u.ss;
+ TALLOC_FREE(sa_list);
+ return true;
+ }
+
+ TALLOC_FREE(sa_list);
+
+ status = internal_resolve_name(talloc_tos(),
+ group,
+ 0x1B,
+ NULL,
+ &sa_list,
+ &count,
+ lp_name_resolve_order());
+ if (NT_STATUS_IS_OK(status)) {
+ *master_ss = sa_list[0].u.ss;
+ TALLOC_FREE(sa_list);
+ return true;
+ }
+
+ TALLOC_FREE(sa_list);
+ return false;
+}
+
+/********************************************************
+ Get the IP address list of the primary domain controller
+ for a domain.
+*********************************************************/
+
+bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ size_t count = 0;
+ NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
+ static const char *ads_order[] = { "ads", NULL };
+ /* Look up #1B name */
+
+ if (lp_security() == SEC_ADS) {
+ status = internal_resolve_name(talloc_tos(),
+ domain,
+ 0x1b,
+ NULL,
+ &sa_list,
+ &count,
+ ads_order);
+ }
+
+ if (!NT_STATUS_IS_OK(status) || count == 0) {
+ TALLOC_FREE(sa_list);
+ status = internal_resolve_name(talloc_tos(),
+ domain,
+ 0x1b,
+ NULL,
+ &sa_list,
+ &count,
+ lp_name_resolve_order());
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(sa_list);
+ return false;
+ }
+ }
+
+ /* if we get more than 1 IP back we have to assume it is a
+ multi-homed PDC and not a mess up */
+
+ if ( count > 1 ) {
+ DBG_INFO("PDC has %zu IP addresses!\n", count);
+ sort_sa_list(sa_list, count);
+ }
+
+ *pss = sa_list[0].u.ss;
+ TALLOC_FREE(sa_list);
+ return true;
+}
+
+/* Private enum type for lookups. */
+
+enum dc_lookup_type { DC_NORMAL_LOOKUP, DC_ADS_ONLY, DC_KDC_ONLY };
+
+/********************************************************
+ Get the IP address list of the domain controllers for
+ a domain.
+*********************************************************/
+
+static NTSTATUS get_dc_list(TALLOC_CTX *ctx,
+ const char *domain,
+ const char *sitename,
+ struct samba_sockaddr **sa_list_ret,
+ size_t *ret_count,
+ enum dc_lookup_type lookup_type,
+ bool *ordered)
+{
+ const char **resolve_order = NULL;
+ char *saf_servername = NULL;
+ char *pserver = NULL;
+ const char *p;
+ char *name;
+ size_t num_addresses = 0;
+ size_t local_count = 0;
+ size_t i;
+ struct samba_sockaddr *auto_sa_list = NULL;
+ struct samba_sockaddr *return_salist = NULL;
+ bool done_auto_lookup = false;
+ size_t auto_count = 0;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ int auto_name_type = 0x1C;
+
+ *ordered = False;
+
+ /* if we are restricted to solely using DNS for looking
+ up a domain controller, make sure that host lookups
+ are enabled for the 'name resolve order'. If host lookups
+ are disabled and ads_only is True, then set the string to
+ NULL. */
+
+ resolve_order = lp_name_resolve_order();
+ if (!resolve_order) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ if (lookup_type == DC_ADS_ONLY) {
+ if (str_list_check_ci(resolve_order, "host")) {
+ static const char *ads_order[] = { "ads", NULL };
+ resolve_order = ads_order;
+
+ /* DNS SRV lookups used by the ads resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ } else {
+ /* this is quite bizarre! */
+ static const char *null_order[] = { "NULL", NULL };
+ resolve_order = null_order;
+ }
+ } else if (lookup_type == DC_KDC_ONLY) {
+ static const char *kdc_order[] = { "kdc", NULL };
+ /* DNS SRV lookups used by the ads/kdc resolver
+ are already sorted by priority and weight */
+ *ordered = true;
+ resolve_order = kdc_order;
+ auto_name_type = KDC_NAME_TYPE;
+ }
+
+ /* fetch the server we have affinity for. Add the
+ 'password server' list to a search for our domain controllers */
+
+ saf_servername = saf_fetch(frame, domain);
+
+ if (strequal(domain, lp_workgroup()) || strequal(domain, lp_realm())) {
+ pserver = talloc_asprintf(frame, "%s, %s",
+ saf_servername ? saf_servername : "",
+ lp_password_server());
+ } else {
+ pserver = talloc_asprintf(frame, "%s, *",
+ saf_servername ? saf_servername : "");
+ }
+
+ TALLOC_FREE(saf_servername);
+ if (!pserver) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ DEBUG(3,("get_dc_list: preferred server list: \"%s\"\n", pserver ));
+
+ /*
+ * if '*' appears in the "password server" list then add
+ * an auto lookup to the list of manually configured
+ * DC's. If any DC is listed by name, then the list should be
+ * considered to be ordered
+ */
+
+ p = pserver;
+ while (next_token_talloc(frame, &p, &name, LIST_SEP)) {
+ if (!done_auto_lookup && strequal(name, "*")) {
+ done_auto_lookup = true;
+
+ status = internal_resolve_name(frame,
+ domain,
+ auto_name_type,
+ sitename,
+ &auto_sa_list,
+ &auto_count,
+ resolve_order);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ /* Wrap check. */
+ if (num_addresses + auto_count < num_addresses) {
+ TALLOC_FREE(auto_sa_list);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ num_addresses += auto_count;
+ DBG_DEBUG("Adding %zu DC's from auto lookup\n",
+ auto_count);
+ } else {
+ /* Wrap check. */
+ if (num_addresses + 1 < num_addresses) {
+ TALLOC_FREE(auto_sa_list);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+ num_addresses++;
+ }
+ }
+
+ /* if we have no addresses and haven't done the auto lookup, then
+ just return the list of DC's. Or maybe we just failed. */
+
+ if (num_addresses == 0) {
+ struct samba_sockaddr *dc_salist = NULL;
+ size_t dc_count = 0;
+
+ if (done_auto_lookup) {
+ DEBUG(4,("get_dc_list: no servers found\n"));
+ status = NT_STATUS_NO_LOGON_SERVERS;
+ goto out;
+ }
+ /* talloc off frame, only move to ctx on success. */
+ status = internal_resolve_name(frame,
+ domain,
+ auto_name_type,
+ sitename,
+ &dc_salist,
+ &dc_count,
+ resolve_order);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ return_salist = dc_salist;
+ local_count = dc_count;
+ goto out;
+ }
+
+ return_salist = talloc_zero_array(frame,
+ struct samba_sockaddr,
+ num_addresses);
+ if (return_salist == NULL) {
+ DEBUG(3,("get_dc_list: malloc fail !\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ p = pserver;
+ local_count = 0;
+
+ /* fill in the return list now with real IP's */
+
+ while ((local_count<num_addresses) &&
+ next_token_talloc(frame, &p, &name, LIST_SEP)) {
+ struct samba_sockaddr name_sa = {0};
+
+ /* copy any addresses from the auto lookup */
+
+ if (strequal(name, "*")) {
+ size_t j;
+ for (j=0; j<auto_count; j++) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &auto_sa_list[j].u.ss);
+ /* Check for and don't copy any
+ * known bad DC IP's. */
+ if(!NT_STATUS_IS_OK(check_negative_conn_cache(
+ domain,
+ addr))) {
+ DEBUG(5,("get_dc_list: "
+ "negative entry %s removed "
+ "from DC list\n",
+ addr));
+ continue;
+ }
+ return_salist[local_count] = auto_sa_list[j];
+ local_count++;
+ }
+ continue;
+ }
+
+ /* explicit lookup; resolve_name() will
+ * handle names & IP addresses */
+ if (resolve_name(name, &name_sa.u.ss, 0x20, true)) {
+ char addr[INET6_ADDRSTRLEN];
+ bool ok;
+
+ /*
+ * Ensure we set sa_socklen correctly.
+ * Doesn't matter now, but eventually we
+ * will remove ip_service and return samba_sockaddr
+ * arrays directly.
+ */
+ ok = sockaddr_storage_to_samba_sockaddr(
+ &name_sa,
+ &name_sa.u.ss);
+ if (!ok) {
+ status = NT_STATUS_INVALID_ADDRESS;
+ goto out;
+ }
+
+ print_sockaddr(addr,
+ sizeof(addr),
+ &name_sa.u.ss);
+
+ /* Check for and don't copy any known bad DC IP's. */
+ if( !NT_STATUS_IS_OK(check_negative_conn_cache(domain,
+ addr)) ) {
+ DEBUG(5,("get_dc_list: negative entry %s "
+ "removed from DC list\n",
+ name ));
+ continue;
+ }
+
+ return_salist[local_count] = name_sa;
+ local_count++;
+ *ordered = true;
+ }
+ }
+
+ /* need to remove duplicates in the list if we have any
+ explicit password servers */
+
+ local_count = remove_duplicate_addrs2(return_salist, local_count );
+
+ /* For DC's we always prioritize IPv4 due to W2K3 not
+ * supporting LDAP, KRB5 or CLDAP over IPv6. */
+
+ if (local_count && return_salist != NULL) {
+ prioritize_ipv4_list(return_salist, local_count);
+ }
+
+ if ( DEBUGLEVEL >= 4 ) {
+ DEBUG(4,("get_dc_list: returning %zu ip addresses "
+ "in an %sordered list\n",
+ local_count,
+ *ordered ? "":"un"));
+ DEBUG(4,("get_dc_list: "));
+ for ( i=0; i<local_count; i++ ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr,
+ sizeof(addr),
+ &return_salist[i].u.ss);
+ DEBUGADD(4,("%s ", addr));
+ }
+ DEBUGADD(4,("\n"));
+ }
+
+ status = (local_count != 0 ? NT_STATUS_OK : NT_STATUS_NO_LOGON_SERVERS);
+
+ out:
+
+ if (NT_STATUS_IS_OK(status)) {
+ *sa_list_ret = talloc_move(ctx, &return_salist);
+ *ret_count = local_count;
+ }
+ TALLOC_FREE(return_salist);
+ TALLOC_FREE(auto_sa_list);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*********************************************************************
+ Small wrapper function to get the DC list and sort it if necessary.
+ Returns a samba_sockaddr array.
+*********************************************************************/
+
+NTSTATUS get_sorted_dc_list(TALLOC_CTX *ctx,
+ const char *domain,
+ const char *sitename,
+ struct samba_sockaddr **sa_list_ret,
+ size_t *ret_count,
+ bool ads_only)
+{
+ bool ordered = false;
+ NTSTATUS status;
+ enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP;
+ struct samba_sockaddr *sa_list = NULL;
+ size_t count = 0;
+
+ DBG_INFO("attempting lookup for name %s (sitename %s)\n",
+ domain,
+ sitename ? sitename : "NULL");
+
+ if (ads_only) {
+ lookup_type = DC_ADS_ONLY;
+ }
+
+ status = get_dc_list(ctx,
+ domain,
+ sitename,
+ &sa_list,
+ &count,
+ lookup_type,
+ &ordered);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS)
+ && sitename) {
+ DBG_WARNING("No server for domain '%s' available"
+ " in site '%s', fallback to all servers\n",
+ domain,
+ sitename);
+ status = get_dc_list(ctx,
+ domain,
+ NULL,
+ &sa_list,
+ &count,
+ lookup_type,
+ &ordered);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if (!ordered) {
+ sort_sa_list(sa_list, count);
+ }
+
+ *ret_count = count;
+ *sa_list_ret = sa_list;
+ return status;
+}
+
+/*********************************************************************
+ Get the KDC list - re-use all the logic in get_dc_list.
+ Returns a samba_sockaddr array.
+*********************************************************************/
+
+NTSTATUS get_kdc_list(TALLOC_CTX *ctx,
+ const char *realm,
+ const char *sitename,
+ struct samba_sockaddr **sa_list_ret,
+ size_t *ret_count)
+{
+ size_t count = 0;
+ struct samba_sockaddr *sa_list = NULL;
+ bool ordered = false;
+ NTSTATUS status;
+
+ status = get_dc_list(ctx,
+ realm,
+ sitename,
+ &sa_list,
+ &count,
+ DC_KDC_ONLY,
+ &ordered);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* only sort if we don't already have an ordered list */
+ if (!ordered ) {
+ sort_sa_list(sa_list, count);
+ }
+
+ *ret_count = count;
+ *sa_list_ret = sa_list;
+ return status;
+}
diff --git a/source3/libsmb/namequery.h b/source3/libsmb/namequery.h
new file mode 100644
index 0000000..16559f9
--- /dev/null
+++ b/source3/libsmb/namequery.h
@@ -0,0 +1,116 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMB_NAMEQUERY_H__
+#define __LIBSMB_NAMEQUERY_H__
+
+#include "includes.h"
+#include <tevent.h>
+
+/* The following definitions come from libsmb/namequery.c */
+bool saf_store( const char *domain, const char *servername );
+bool saf_join_store( const char *domain, const char *servername );
+bool saf_delete( const char *domain );
+char *saf_fetch(TALLOC_CTX *mem_ctx, const char *domain );
+struct tevent_req *node_status_query_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct nmb_name *name,
+ const struct sockaddr_storage *addr);
+NTSTATUS node_status_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct node_status **pnode_status,
+ size_t *pnum_names,
+ struct node_status_extra *extra);
+NTSTATUS node_status_query(TALLOC_CTX *mem_ctx, struct nmb_name *name,
+ const struct sockaddr_storage *addr,
+ struct node_status **pnode_status,
+ size_t *pnum_names,
+ struct node_status_extra *extra);
+bool name_status_find(const char *q_name,
+ int q_type,
+ int type,
+ const struct sockaddr_storage *to_ss,
+ fstring name);
+size_t remove_duplicate_addrs2(struct samba_sockaddr *salist, size_t count);
+struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name, int name_type,
+ bool bcast, bool recurse,
+ const struct sockaddr_storage *addr);
+NTSTATUS name_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs, size_t *num_addrs,
+ uint8_t *flags);
+NTSTATUS name_query(const char *name, int name_type,
+ bool bcast, bool recurse,
+ const struct sockaddr_storage *to_ss,
+ TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs, uint8_t *flags);
+struct tevent_req *name_resolve_bcast_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name,
+ int name_type);
+NTSTATUS name_resolve_bcast_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs);
+NTSTATUS name_resolve_bcast(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_iplist,
+ size_t *return_count);
+struct tevent_req *resolve_wins_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *name,
+ int name_type);
+NTSTATUS resolve_wins_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct sockaddr_storage **addrs,
+ size_t *num_addrs, uint8_t *flags);
+NTSTATUS resolve_wins(TALLOC_CTX *mem_ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_iplist,
+ size_t *return_count);
+NTSTATUS internal_resolve_name(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ const char *sitename,
+ struct samba_sockaddr **return_salist,
+ size_t *ret_count,
+ const char **resolve_order);
+bool resolve_name(const char *name,
+ struct sockaddr_storage *return_ss,
+ int name_type,
+ bool prefer_ipv4);
+NTSTATUS resolve_name_list(TALLOC_CTX *ctx,
+ const char *name,
+ int name_type,
+ struct sockaddr_storage **return_ss_arr,
+ unsigned int *p_num_entries);
+bool find_master_ip(const char *group, struct sockaddr_storage *master_ss);
+bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss);
+NTSTATUS get_sorted_dc_list(TALLOC_CTX *ctx,
+ const char *domain,
+ const char *sitename,
+ struct samba_sockaddr **sa_list_ret,
+ size_t *ret_count,
+ bool ads_only);
+NTSTATUS get_kdc_list(TALLOC_CTX *ctx,
+ const char *realm,
+ const char *sitename,
+ struct samba_sockaddr **sa_list_ret,
+ size_t *ret_count);
+#endif
diff --git a/source3/libsmb/namequery_dc.c b/source3/libsmb/namequery_dc.c
new file mode 100644
index 0000000..06e2c50
--- /dev/null
+++ b/source3/libsmb/namequery_dc.c
@@ -0,0 +1,262 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind daemon connection manager
+
+ Copyright (C) Tim Potter 2001
+ Copyright (C) Andrew Bartlett 2002
+ Copyright (C) Gerald Carter 2003
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+#include "libsmb/namequery.h"
+#include "libads/sitename_cache.h"
+#include "ads.h"
+#include "../librpc/gen_ndr/nbt.h"
+#include "lib/param/loadparm.h"
+#include "lib/util/string_wrappers.h"
+
+/**********************************************************************
+ Is this our primary domain ?
+**********************************************************************/
+
+#ifdef HAVE_ADS
+static bool is_our_primary_domain(const char *domain)
+{
+ int role = lp_server_role();
+
+ if ((role == ROLE_DOMAIN_MEMBER) && strequal(lp_workgroup(), domain)) {
+ return True;
+ } else if (strequal(get_global_sam_name(), domain)) {
+ return True;
+ }
+ return False;
+}
+#endif
+
+/**************************************************************************
+ Find the name and IP address for a server in the realm/domain
+ *************************************************************************/
+
+static bool ads_dc_name(const char *domain,
+ const char *realm,
+ struct sockaddr_storage *dc_ss,
+ fstring srv_name)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ bool ok = false;
+ ADS_STRUCT *ads;
+ char *sitename;
+ int i;
+ char addr[INET6_ADDRSTRLEN];
+
+ if (!realm && strequal(domain, lp_workgroup())) {
+ realm = lp_realm();
+ }
+
+ sitename = sitename_fetch(tmp_ctx, realm);
+
+ /* Try this 3 times then give up. */
+ for( i =0 ; i < 3; i++) {
+ ads = ads_init(tmp_ctx, realm, domain, NULL, ADS_SASL_PLAIN);
+ if (!ads) {
+ ok = false;
+ goto out;
+ }
+
+ DEBUG(4,("ads_dc_name: domain=%s\n", domain));
+
+#ifdef HAVE_ADS
+ /* we don't need to bind, just connect */
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+ ads_connect(ads);
+#endif
+
+ if (!ads->config.realm) {
+ ok = false;
+ goto out;
+ }
+
+ /* Now we've found a server, see if our sitename
+ has changed. If so, we need to re-do the DNS query
+ to ensure we only find servers in our site. */
+
+ if (stored_sitename_changed(realm, sitename)) {
+ sitename = sitename_fetch(tmp_ctx, realm);
+ TALLOC_FREE(ads);
+ /* Ensure we don't cache the DC we just connected to. */
+ namecache_delete(realm, 0x1C);
+ namecache_delete(domain, 0x1C);
+ continue;
+ }
+
+#ifdef HAVE_ADS
+ if (is_our_primary_domain(domain) && (ads->config.flags & NBT_SERVER_KDC)) {
+ if (ads_closest_dc(ads)) {
+ /* We're going to use this KDC for this realm/domain.
+ If we are using sites, then force the krb5 libs
+ to use this KDC. */
+
+ create_local_private_krb5_conf_for_domain(realm,
+ domain,
+ sitename,
+ &ads->ldap.ss);
+ } else {
+ create_local_private_krb5_conf_for_domain(realm,
+ domain,
+ NULL,
+ &ads->ldap.ss);
+ }
+ }
+#endif
+ break;
+ }
+
+ if (i == 3) {
+ DEBUG(1,("ads_dc_name: sitename (now \"%s\") keeps changing ???\n",
+ sitename ? sitename : ""));
+ ok = false;
+ goto out;
+ }
+
+ fstrcpy(srv_name, ads->config.ldap_server_name);
+ if (!strupper_m(srv_name)) {
+ ok = false;
+ goto out;
+ }
+#ifdef HAVE_ADS
+ *dc_ss = ads->ldap.ss;
+#else
+ zero_sockaddr(dc_ss);
+#endif
+ print_sockaddr(addr, sizeof(addr), dc_ss);
+ DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n",
+ srv_name, addr));
+
+ ok = true;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ok;
+}
+
+/****************************************************************************
+ Utility function to return the name of a DC. The name is guaranteed to be
+ valid since we have already done a name_status_find on it
+ ***************************************************************************/
+
+static bool rpc_dc_name(const char *domain,
+ fstring srv_name,
+ struct sockaddr_storage *ss_out)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ size_t count = 0;
+ struct sockaddr_storage dc_ss;
+ size_t i;
+ NTSTATUS result;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* get a list of all domain controllers */
+
+ result = get_sorted_dc_list(talloc_tos(),
+ domain,
+ NULL,
+ &sa_list,
+ &count,
+ false);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
+ return False;
+ }
+
+ /* Remove the entry we've already failed with (should be the PDC). */
+
+ for (i = 0; i < count; i++) {
+ if (is_zero_addr(&sa_list[i].u.ss))
+ continue;
+
+ if (name_status_find(domain, 0x1c, 0x20, &sa_list[i].u.ss, srv_name)) {
+ result = check_negative_conn_cache( domain, srv_name );
+ if ( NT_STATUS_IS_OK(result) ) {
+ dc_ss = sa_list[i].u.ss;
+ goto done;
+ }
+ }
+ }
+
+ TALLOC_FREE(sa_list);
+
+ /* No-one to talk to )-: */
+ return False; /* Boo-hoo */
+
+ done:
+ /* We have the netbios name and IP address of a domain controller.
+ Ideally we should sent a SAMLOGON request to determine whether
+ the DC is alive and kicking. If we can catch a dead DC before
+ performing a cli_connect() we can avoid a 30-second timeout. */
+
+ print_sockaddr(addr, sizeof(addr), &dc_ss);
+ DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
+ addr, domain));
+
+ *ss_out = dc_ss;
+ TALLOC_FREE(sa_list);
+
+ return True;
+}
+
+/**********************************************************************
+ wrapper around ads and rpc methods of finds DC's
+**********************************************************************/
+
+bool get_dc_name(const char *domain,
+ const char *realm,
+ fstring srv_name,
+ struct sockaddr_storage *ss_out)
+{
+ struct sockaddr_storage dc_ss;
+ bool ret;
+ bool our_domain = False;
+
+ zero_sockaddr(&dc_ss);
+
+ ret = False;
+
+ if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) )
+ our_domain = True;
+
+ /* always try to obey what the admin specified in smb.conf
+ (for the local domain) */
+
+ if ( (our_domain && lp_security()==SEC_ADS) || realm ) {
+ ret = ads_dc_name(domain, realm, &dc_ss, srv_name);
+ }
+
+ if (!domain) {
+ /* if we have only the realm we can't do anything else */
+ return False;
+ }
+
+ if (!ret) {
+ /* fall back on rpc methods if the ADS methods fail */
+ ret = rpc_dc_name(domain, srv_name, &dc_ss);
+ }
+
+ *ss_out = dc_ss;
+
+ return ret;
+}
diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c
new file mode 100644
index 0000000..c90e92e
--- /dev/null
+++ b/source3/libsmb/nmblib.c
@@ -0,0 +1,1466 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libsmb/nmblib.h"
+#include "lib/util/string_wrappers.h"
+
+static const struct opcode_names {
+ const char *nmb_opcode_name;
+ int opcode;
+} nmb_header_opcode_names[] = {
+ {"Query", 0 },
+ {"Registration", 5 },
+ {"Release", 6 },
+ {"WACK", 7 },
+ {"Refresh", 8 },
+ {"Refresh(altcode)", 9 },
+ {"Multi-homed Registration", 15 },
+ {0, -1 }
+};
+
+/****************************************************************************
+ Lookup a nmb opcode name.
+****************************************************************************/
+
+static const char *lookup_opcode_name( int opcode )
+{
+ const struct opcode_names *op_namep;
+ int i;
+
+ for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) {
+ op_namep = &nmb_header_opcode_names[i];
+ if(opcode == op_namep->opcode)
+ return op_namep->nmb_opcode_name;
+ }
+ return "<unknown opcode>";
+}
+
+/****************************************************************************
+ Print out a res_rec structure.
+****************************************************************************/
+
+static void debug_nmb_res_rec(struct res_rec *res, const char *hdr)
+{
+ int i, j;
+
+ DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n",
+ hdr,
+ nmb_namestr(&res->rr_name),
+ res->rr_type,
+ res->rr_class,
+ res->ttl ) );
+
+ if (res->rdlength == 0) {
+ return;
+ }
+
+ for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) {
+ DEBUGADD(4, (" %s %3x char ", hdr, i));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ unsigned char x = res->rdata[i+j];
+ if (x < 32 || x > 127)
+ x = '.';
+
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%c", x));
+ }
+
+ DEBUGADD(4, (" hex "));
+
+ for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) {
+ if (i+j >= res->rdlength)
+ break;
+ DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j]));
+ }
+
+ DEBUGADD(4, ("\n"));
+ }
+}
+
+/****************************************************************************
+ Process a nmb packet.
+****************************************************************************/
+
+void debug_nmb_packet(struct packet_struct *p)
+{
+ struct nmb_packet *nmb = &p->packet.nmb;
+
+ if( DEBUGLVL( 4 ) ) {
+ dbgtext( "nmb packet from %s(%d) header: id=%d "
+ "opcode=%s(%d) response=%s\n",
+ inet_ntoa(p->ip), p->port,
+ nmb->header.name_trn_id,
+ lookup_opcode_name(nmb->header.opcode),
+ nmb->header.opcode,
+ BOOLSTR(nmb->header.response) );
+ dbgtext( " header: flags: bcast=%s rec_avail=%s "
+ "rec_des=%s trunc=%s auth=%s\n",
+ BOOLSTR(nmb->header.nm_flags.bcast),
+ BOOLSTR(nmb->header.nm_flags.recursion_available),
+ BOOLSTR(nmb->header.nm_flags.recursion_desired),
+ BOOLSTR(nmb->header.nm_flags.trunc),
+ BOOLSTR(nmb->header.nm_flags.authoritative) );
+ dbgtext( " header: rcode=%d qdcount=%d ancount=%d "
+ "nscount=%d arcount=%d\n",
+ nmb->header.rcode,
+ nmb->header.qdcount,
+ nmb->header.ancount,
+ nmb->header.nscount,
+ nmb->header.arcount );
+ }
+
+ if (nmb->header.qdcount) {
+ DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n",
+ nmb_namestr(&nmb->question.question_name),
+ nmb->question.question_type,
+ nmb->question.question_class) );
+ }
+
+ if (nmb->answers && nmb->header.ancount) {
+ debug_nmb_res_rec(nmb->answers,"answers");
+ }
+ if (nmb->nsrecs && nmb->header.nscount) {
+ debug_nmb_res_rec(nmb->nsrecs,"nsrecs");
+ }
+ if (nmb->additional && nmb->header.arcount) {
+ debug_nmb_res_rec(nmb->additional,"additional");
+ }
+}
+
+/*******************************************************************
+ Handle "compressed" name pointers.
+******************************************************************/
+
+static bool handle_name_ptrs(unsigned char *ubuf,int *offset,int length,
+ bool *got_pointer,int *ret)
+{
+ int loop_count=0;
+
+ while ((ubuf[*offset] & 0xC0) == 0xC0) {
+ if (!*got_pointer)
+ (*ret) += 2;
+ (*got_pointer)=True;
+ if (*offset > length - 2) {
+ return False;
+ }
+ (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1];
+ if (loop_count++ == 10 ||
+ (*offset) < 0 || (*offset)>(length-2)) {
+ return False;
+ }
+ }
+ return True;
+}
+
+/*******************************************************************
+ Parse a nmb name from "compressed" format to something readable
+ return the space taken by the name, or 0 if the name is invalid
+******************************************************************/
+
+static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name)
+{
+ size_t m,n=0;
+ unsigned char *ubuf = (unsigned char *)inbuf;
+ int ret = 0;
+ bool got_pointer=False;
+ size_t loop_count=0;
+ int offset = ofs;
+
+ if (length - offset < 2)
+ return(0);
+
+ /* handle initial name pointers */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+
+ /* m must be 32 to exactly fill in the 16 bytes of the netbios name */
+ if (m != 32) {
+ return 0;
+ }
+ /* Cannot go past length. */
+ if (offset+m+2 > length) {
+ return 0;
+ }
+
+ memset((char *)name,'\0',sizeof(*name));
+
+ /* the "compressed" part */
+ if (!got_pointer)
+ ret += m + 2;
+ offset++;
+ while (m > 0) {
+ unsigned char c1,c2;
+ c1 = ubuf[offset++]-'A';
+ c2 = ubuf[offset++]-'A';
+ if ((c1 & 0xF0) || (c2 & 0xF0)) {
+ return(0);
+ }
+ if (n >= sizeof(name->name)) {
+ return 0;
+ }
+ name->name[n++] = (c1<<4) | c2;
+ m -= 2;
+ }
+ /*
+ * RFC1002: For a valid NetBIOS name, exiting from the above,
+ * n *must* be MAX_NETBIOSNAME_LEN (16).
+ */
+ if (n != MAX_NETBIOSNAME_LEN) {
+ return 0;
+ }
+
+ /* parse out the name type, its always
+ * in the 16th byte of the name */
+ name->name_type = ((unsigned char)name->name[15]) & 0xff;
+
+ /* remove trailing spaces */
+ name->name[15] = 0;
+ n = 14;
+ while (n && name->name[n]==' ')
+ name->name[n--] = 0;
+
+ /* now the domain parts (if any) */
+ n = 0;
+ while (ubuf[offset]) {
+ /* we can have pointers within the domain part as well */
+ if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret))
+ return(0);
+
+ m = ubuf[offset];
+ /*
+ * Don't allow null domain parts.
+ */
+ if (!m)
+ return(0);
+ if (!got_pointer)
+ ret += m+1;
+ if (n)
+ name->scope[n++] = '.';
+ if (m+2+offset>length || n+m+1>sizeof(name->scope))
+ return(0);
+ offset++;
+ while (m--)
+ name->scope[n++] = (char)ubuf[offset++];
+
+ /*
+ * Watch for malicious loops.
+ */
+ if (loop_count++ == 10)
+ return 0;
+ }
+ name->scope[n++] = 0;
+
+ return(ret);
+}
+
+/****************************************************************************
+ Put a netbios name, padding(s) and a name type into a 16 character buffer.
+ name is already in DOS charset.
+ [15 bytes name + padding][1 byte name type].
+****************************************************************************/
+
+void put_name(char *dest, const char *name, int pad, unsigned int name_type)
+{
+ size_t len = strlen(name);
+
+ memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ?
+ len : MAX_NETBIOSNAME_LEN - 1);
+ if (len < MAX_NETBIOSNAME_LEN - 1) {
+ memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len);
+ }
+ dest[MAX_NETBIOSNAME_LEN - 1] = name_type;
+}
+
+/*******************************************************************
+ Put a compressed nmb name into a buffer. Return the length of the
+ compressed name.
+
+ Compressed names are really weird. The "compression" doubles the
+ size. The idea is that it also means that compressed names conform
+ to the domain name system. See RFC1002.
+
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_nmb_name(char *buf, size_t buflen, int offset,struct nmb_name *name)
+{
+ int ret,m;
+ nstring buf1;
+ char *p;
+
+ if (strcmp(name->name,"*") == 0) {
+ /* special case for wildcard name */
+ put_name(buf1, "*", '\0', name->name_type);
+ } else {
+ put_name(buf1, name->name, ' ', name->name_type);
+ }
+
+ if (buf) {
+ if (offset >= buflen) {
+ return 0;
+ }
+ buf[offset] = 0x20;
+ }
+
+ ret = 34;
+
+ for (m=0;m<MAX_NETBIOSNAME_LEN;m++) {
+ if (buf) {
+ if (offset+2+2*m >= buflen) {
+ return 0;
+ }
+ buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF);
+ buf[offset+2+2*m] = 'A' + (buf1[m]&0xF);
+ }
+ }
+ offset += 33;
+
+ if (buf) {
+ if (offset >= buflen) {
+ return 0;
+ }
+ buf[offset] = 0;
+ }
+
+ if (name->scope[0]) {
+ /* XXXX this scope handling needs testing */
+ size_t scopenamelen = strlen(name->scope) + 1;
+ ret += scopenamelen;
+ if (buf) {
+ if (offset+1+scopenamelen >= buflen) {
+ return 0;
+ }
+ strlcpy(&buf[offset+1],name->scope,
+ buflen - (offset+1));
+
+ p = &buf[offset+1];
+ while ((p = strchr_m(p,'.'))) {
+ buf[offset] = PTR_DIFF(p,&buf[offset+1]);
+ offset += (buf[offset] + 1);
+ if (offset+1 >= buflen) {
+ return 0;
+ }
+ p = &buf[offset+1];
+ }
+ buf[offset] = strlen(&buf[offset+1]);
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Useful for debugging messages.
+******************************************************************/
+
+char *nmb_namestr(const struct nmb_name *n)
+{
+ fstring name;
+ char *result;
+
+ pull_ascii_fstring(name, n->name);
+ if (!n->scope[0])
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>", name,
+ n->name_type);
+ else
+ result = talloc_asprintf(talloc_tos(), "%s<%02x>.%s", name,
+ n->name_type, n->scope);
+
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*******************************************************************
+ Allocate and parse some resource records.
+******************************************************************/
+
+static bool parse_alloc_res_rec(char *inbuf,int *offset,int length,
+ struct res_rec **recs, int count)
+{
+ int i;
+
+ *recs = SMB_MALLOC_ARRAY(struct res_rec, count);
+ if (!*recs)
+ return(False);
+
+ memset((char *)*recs,'\0',sizeof(**recs)*count);
+
+ for (i=0;i<count;i++) {
+ int l = parse_nmb_name(inbuf,*offset,length,
+ &(*recs)[i].rr_name);
+ (*offset) += l;
+ if (!l || (*offset)+10 > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ (*recs)[i].rr_type = RSVAL(inbuf,(*offset));
+ (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2);
+ (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4);
+ (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8);
+ (*offset) += 10;
+ if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) ||
+ (*offset)+(*recs)[i].rdlength > length) {
+ SAFE_FREE(*recs);
+ return(False);
+ }
+ memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength);
+ (*offset) += (*recs)[i].rdlength;
+ }
+ return(True);
+}
+
+/*******************************************************************
+ Put a resource record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_res_rec(char *buf, size_t buflen, int offset,struct res_rec *recs,int count)
+{
+ int ret=0;
+ int i;
+
+ for (i=0;i<count;i++) {
+ int l = put_nmb_name(buf,buflen,offset,&recs[i].rr_name);
+ offset += l;
+ ret += l;
+ if (buf) {
+ RSSVAL(buf,offset,recs[i].rr_type);
+ RSSVAL(buf,offset+2,recs[i].rr_class);
+ RSIVAL(buf,offset+4,(unsigned int)recs[i].ttl);
+ RSSVAL(buf,offset+8,recs[i].rdlength);
+ memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength);
+ }
+ offset += 10+recs[i].rdlength;
+ ret += 10+recs[i].rdlength;
+ }
+
+ return ret;
+}
+
+/*******************************************************************
+ Put a compressed name pointer record into a packet.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int put_compressed_name_ptr(unsigned char *buf,
+ int offset,
+ struct res_rec *rec,
+ int ptr_offset)
+{
+ int ret=offset;
+ if (buf) {
+ buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF));
+ buf[offset+1] = (ptr_offset & 0xFF);
+ }
+ offset += 2;
+ if (buf) {
+ RSSVAL(buf,offset,rec->rr_type);
+ RSSVAL(buf,offset+2,rec->rr_class);
+ RSIVAL(buf,offset+4,rec->ttl);
+ RSSVAL(buf,offset+8,rec->rdlength);
+ memcpy(buf+offset+10,rec->rdata,rec->rdlength);
+ }
+ offset += 10+rec->rdlength;
+ ret = (offset - ret);
+
+ return ret;
+}
+
+/*******************************************************************
+ Parse a dgram packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+
+ This is documented in section 4.4.1 of RFC1002.
+******************************************************************/
+
+static bool parse_dgram(char *inbuf,int length,struct dgram_packet *dgram)
+{
+ size_t offset;
+ int flags;
+
+ memset((char *)dgram,'\0',sizeof(*dgram));
+
+ if (length < 14)
+ return(False);
+
+ dgram->header.msg_type = CVAL(inbuf,0);
+ flags = CVAL(inbuf,1);
+ dgram->header.flags.node_type = (enum node_type)((flags>>2)&3);
+ if (flags & 1)
+ dgram->header.flags.more = True;
+ if (flags & 2)
+ dgram->header.flags.first = True;
+ dgram->header.dgm_id = RSVAL(inbuf,2);
+ putip((char *)&dgram->header.source_ip,inbuf+4);
+ dgram->header.source_port = RSVAL(inbuf,8);
+ dgram->header.dgm_length = RSVAL(inbuf,10);
+ dgram->header.packet_offset = RSVAL(inbuf,12);
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->source_name);
+ offset += parse_nmb_name(inbuf,offset,length,
+ &dgram->dest_name);
+ }
+
+ if (offset >= length || (length-offset > sizeof(dgram->data)))
+ return(False);
+
+ dgram->datasize = length-offset;
+ memcpy(dgram->data,inbuf+offset,dgram->datasize);
+
+ /* Paranioa. Ensure the last 2 bytes in the dgram buffer are
+ zero. This should be true anyway, just enforce it for
+ paranioa sake. JRA. */
+ SMB_ASSERT(dgram->datasize <= (sizeof(dgram->data)-2));
+ memset(&dgram->data[sizeof(dgram->data)-2], '\0', 2);
+
+ return(True);
+}
+
+/*******************************************************************
+ Parse a nmb packet. Return False if the packet can't be parsed
+ or is invalid for some reason, True otherwise.
+******************************************************************/
+
+static bool parse_nmb(char *inbuf,int length,struct nmb_packet *nmb)
+{
+ int nm_flags,offset;
+
+ memset((char *)nmb,'\0',sizeof(*nmb));
+
+ if (length < 12)
+ return(False);
+
+ /* parse the header */
+ nmb->header.name_trn_id = RSVAL(inbuf,0);
+
+ DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id));
+
+ nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF;
+ nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False;
+ nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4);
+ nmb->header.nm_flags.bcast = (nm_flags&1)?True:False;
+ nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False;
+ nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False;
+ nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False;
+ nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False;
+ nmb->header.rcode = CVAL(inbuf,3) & 0xF;
+ nmb->header.qdcount = RSVAL(inbuf,4);
+ nmb->header.ancount = RSVAL(inbuf,6);
+ nmb->header.nscount = RSVAL(inbuf,8);
+ nmb->header.arcount = RSVAL(inbuf,10);
+
+ if (nmb->header.qdcount) {
+ offset = parse_nmb_name(inbuf,12,length,
+ &nmb->question.question_name);
+ if (!offset)
+ return(False);
+
+ if (length - (12+offset) < 4)
+ return(False);
+ nmb->question.question_type = RSVAL(inbuf,12+offset);
+ nmb->question.question_class = RSVAL(inbuf,12+offset+2);
+
+ offset += 12+4;
+ } else {
+ offset = 12;
+ }
+
+ /* and any resource records */
+ if (nmb->header.ancount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers,
+ nmb->header.ancount))
+ return(False);
+
+ if (nmb->header.nscount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs,
+ nmb->header.nscount))
+ return(False);
+
+ if (nmb->header.arcount &&
+ !parse_alloc_res_rec(inbuf,&offset,length,
+ &nmb->additional, nmb->header.arcount))
+ return(False);
+
+ return(True);
+}
+
+/*******************************************************************
+ 'Copy constructor' for an nmb packet.
+******************************************************************/
+
+static struct packet_struct *copy_nmb_packet(struct packet_struct *packet)
+{
+ struct nmb_packet *nmb;
+ struct nmb_packet *copy_nmb;
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) {
+ DEBUG(0,("copy_nmb_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+ pkt_copy->recv_fd = -1;
+ pkt_copy->send_fd = -1;
+
+ /* Ensure this copy has no resource records. */
+ nmb = &packet->packet.nmb;
+ copy_nmb = &pkt_copy->packet.nmb;
+
+ copy_nmb->answers = NULL;
+ copy_nmb->nsrecs = NULL;
+ copy_nmb->additional = NULL;
+
+ /* Now copy any resource records. */
+
+ if (nmb->answers) {
+ if((copy_nmb->answers = SMB_MALLOC_ARRAY(
+ struct res_rec,nmb->header.ancount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->answers, (char *)nmb->answers,
+ nmb->header.ancount * sizeof(struct res_rec));
+ }
+ if (nmb->nsrecs) {
+ if((copy_nmb->nsrecs = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.nscount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs,
+ nmb->header.nscount * sizeof(struct res_rec));
+ }
+ if (nmb->additional) {
+ if((copy_nmb->additional = SMB_MALLOC_ARRAY(
+ struct res_rec, nmb->header.arcount)) == NULL)
+ goto free_and_exit;
+ memcpy((char *)copy_nmb->additional, (char *)nmb->additional,
+ nmb->header.arcount * sizeof(struct res_rec));
+ }
+
+ return pkt_copy;
+
+ free_and_exit:
+
+ SAFE_FREE(copy_nmb->answers);
+ SAFE_FREE(copy_nmb->nsrecs);
+ SAFE_FREE(copy_nmb->additional);
+ SAFE_FREE(pkt_copy);
+
+ DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n"));
+ return NULL;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a dgram packet.
+******************************************************************/
+
+static struct packet_struct *copy_dgram_packet(struct packet_struct *packet)
+{
+ struct packet_struct *pkt_copy;
+
+ if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) {
+ DEBUG(0,("copy_dgram_packet: malloc fail.\n"));
+ return NULL;
+ }
+
+ /* Structure copy of entire thing. */
+
+ *pkt_copy = *packet;
+
+ /* Ensure this copy is not locked. */
+ pkt_copy->locked = False;
+ pkt_copy->recv_fd = -1;
+ pkt_copy->send_fd = -1;
+
+ /* There are no additional pointers in a dgram packet,
+ we are finished. */
+ return pkt_copy;
+}
+
+/*******************************************************************
+ 'Copy constructor' for a generic packet.
+******************************************************************/
+
+struct packet_struct *copy_packet(struct packet_struct *packet)
+{
+ if(packet->packet_type == NMB_PACKET)
+ return copy_nmb_packet(packet);
+ else if (packet->packet_type == DGRAM_PACKET)
+ return copy_dgram_packet(packet);
+ return NULL;
+}
+
+/*******************************************************************
+ Free up any resources associated with an nmb packet.
+******************************************************************/
+
+static void free_nmb_packet(struct nmb_packet *nmb)
+{
+ SAFE_FREE(nmb->answers);
+ SAFE_FREE(nmb->nsrecs);
+ SAFE_FREE(nmb->additional);
+}
+
+/*******************************************************************
+ Free up any resources associated with a dgram packet.
+******************************************************************/
+
+static void free_dgram_packet(struct dgram_packet *nmb)
+{
+ /* We have nothing to do for a dgram packet. */
+}
+
+/*******************************************************************
+ Free up any resources associated with a packet.
+******************************************************************/
+
+void free_packet(struct packet_struct *packet)
+{
+ if (packet->locked)
+ return;
+ if (packet->packet_type == NMB_PACKET)
+ free_nmb_packet(&packet->packet.nmb);
+ else if (packet->packet_type == DGRAM_PACKET)
+ free_dgram_packet(&packet->packet.dgram);
+ ZERO_STRUCTPN(packet);
+ SAFE_FREE(packet);
+}
+
+int packet_trn_id(struct packet_struct *p)
+{
+ int result;
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ result = p->packet.nmb.header.name_trn_id;
+ break;
+ case DGRAM_PACKET:
+ result = p->packet.dgram.header.dgm_id;
+ break;
+ default:
+ result = -1;
+ }
+ return result;
+}
+
+/*******************************************************************
+ Parse a packet buffer into a packet structure.
+******************************************************************/
+
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port)
+{
+ struct packet_struct *p;
+ bool ok=False;
+
+ p = SMB_MALLOC_P(struct packet_struct);
+ if (!p)
+ return(NULL);
+
+ ZERO_STRUCTP(p); /* initialize for possible padding */
+
+ p->next = NULL;
+ p->prev = NULL;
+ p->ip = ip;
+ p->port = port;
+ p->locked = False;
+ p->timestamp = time(NULL);
+ p->packet_type = packet_type;
+
+ switch (packet_type) {
+ case NMB_PACKET:
+ ok = parse_nmb(buf,length,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ ok = parse_dgram(buf,length,&p->packet.dgram);
+ break;
+ }
+
+ if (!ok) {
+ free_packet(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+static struct packet_struct *copy_packet_talloc(
+ TALLOC_CTX *mem_ctx, const struct packet_struct *src)
+{
+ struct packet_struct *pkt;
+
+ pkt = talloc_memdup(mem_ctx, src, sizeof(struct packet_struct));
+ if (pkt == NULL) {
+ return NULL;
+ }
+ pkt->locked = false;
+ pkt->recv_fd = -1;
+ pkt->send_fd = -1;
+
+ if (src->packet_type == NMB_PACKET) {
+ const struct nmb_packet *nsrc = &src->packet.nmb;
+ struct nmb_packet *ndst = &pkt->packet.nmb;
+
+ if (nsrc->answers != NULL) {
+ ndst->answers = talloc_memdup(
+ pkt, nsrc->answers,
+ sizeof(struct res_rec) * nsrc->header.ancount);
+ if (ndst->answers == NULL) {
+ goto fail;
+ }
+ }
+ if (nsrc->nsrecs != NULL) {
+ ndst->nsrecs = talloc_memdup(
+ pkt, nsrc->nsrecs,
+ sizeof(struct res_rec) * nsrc->header.nscount);
+ if (ndst->nsrecs == NULL) {
+ goto fail;
+ }
+ }
+ if (nsrc->additional != NULL) {
+ ndst->additional = talloc_memdup(
+ pkt, nsrc->additional,
+ sizeof(struct res_rec) * nsrc->header.arcount);
+ if (ndst->additional == NULL) {
+ goto fail;
+ }
+ }
+ }
+
+ return pkt;
+
+ /*
+ * DGRAM packets have no substructures
+ */
+
+fail:
+ TALLOC_FREE(pkt);
+ return NULL;
+}
+
+struct packet_struct *parse_packet_talloc(TALLOC_CTX *mem_ctx,
+ char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port)
+{
+ struct packet_struct *pkt, *result;
+
+ pkt = parse_packet(buf, length, packet_type, ip, port);
+ if (pkt == NULL) {
+ return NULL;
+ }
+ result = copy_packet_talloc(mem_ctx, pkt);
+ free_packet(pkt);
+ return result;
+}
+
+/*******************************************************************
+ Send a udp packet on a already open socket.
+******************************************************************/
+
+static bool send_udp(int fd,char *buf,int len,struct in_addr ip,int port)
+{
+ bool ret = False;
+ int i;
+ struct sockaddr_in sock_out;
+
+ /* set the address and port */
+ memset((char *)&sock_out,'\0',sizeof(sock_out));
+ putip((char *)&sock_out.sin_addr,(char *)&ip);
+ sock_out.sin_port = htons( port );
+ sock_out.sin_family = AF_INET;
+
+ DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n",
+ len, inet_ntoa(ip), port ) );
+
+ /*
+ * Patch to fix asynch error notifications from Linux kernel.
+ */
+
+ for (i = 0; i < 5; i++) {
+ ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out,
+ sizeof(sock_out)) >= 0);
+ if (ret || errno != ECONNREFUSED)
+ break;
+ }
+
+ if (!ret)
+ DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n",
+ inet_ntoa(ip),port,strerror(errno)));
+
+ return(ret);
+}
+
+/*******************************************************************
+ Build a dgram packet ready for sending.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_dgram(char *buf, size_t len, struct dgram_packet *dgram)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ /* put in the header */
+ if (buf) {
+ ubuf[0] = dgram->header.msg_type;
+ ubuf[1] = (((int)dgram->header.flags.node_type)<<2);
+ if (dgram->header.flags.more)
+ ubuf[1] |= 1;
+ if (dgram->header.flags.first)
+ ubuf[1] |= 2;
+ RSSVAL(ubuf,2,dgram->header.dgm_id);
+ putip(ubuf+4,(char *)&dgram->header.source_ip);
+ RSSVAL(ubuf,8,dgram->header.source_port);
+ RSSVAL(ubuf,12,dgram->header.packet_offset);
+ }
+
+ offset = 14;
+
+ if (dgram->header.msg_type == 0x10 ||
+ dgram->header.msg_type == 0x11 ||
+ dgram->header.msg_type == 0x12) {
+ offset += put_nmb_name((char *)ubuf,len,offset,&dgram->source_name);
+ offset += put_nmb_name((char *)ubuf,len,offset,&dgram->dest_name);
+ }
+
+ if (buf) {
+ memcpy(ubuf+offset,dgram->data,dgram->datasize);
+ }
+ offset += dgram->datasize;
+
+ /* automatically set the dgm_length
+ * NOTE: RFC1002 says the dgm_length does *not*
+ * include the fourteen-byte header. crh
+ */
+ dgram->header.dgm_length = (offset - 14);
+ if (buf) {
+ RSSVAL(ubuf,10,dgram->header.dgm_length);
+ }
+
+ return offset;
+}
+
+/*******************************************************************
+ Build a nmb name
+*******************************************************************/
+
+void make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ (void)strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, lp_netbios_scope(), 64, STR_TERMINATE);
+}
+
+/*******************************************************************
+ Compare two nmb names
+******************************************************************/
+
+bool nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2)
+{
+ return ((n1->name_type == n2->name_type) &&
+ strequal(n1->name ,n2->name ) &&
+ strequal(n1->scope,n2->scope));
+}
+
+/*******************************************************************
+ Build a nmb packet ready for sending.
+ If buf == NULL this is a length calculation.
+******************************************************************/
+
+static int build_nmb(char *buf, size_t len, struct nmb_packet *nmb)
+{
+ unsigned char *ubuf = (unsigned char *)buf;
+ int offset=0;
+
+ if (len && len < 12) {
+ return 0;
+ }
+
+ /* put in the header */
+ if (buf) {
+ RSSVAL(ubuf,offset,nmb->header.name_trn_id);
+ ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3;
+ if (nmb->header.response)
+ ubuf[offset+2] |= (1<<7);
+ if (nmb->header.nm_flags.authoritative &&
+ nmb->header.response)
+ ubuf[offset+2] |= 0x4;
+ if (nmb->header.nm_flags.trunc)
+ ubuf[offset+2] |= 0x2;
+ if (nmb->header.nm_flags.recursion_desired)
+ ubuf[offset+2] |= 0x1;
+ if (nmb->header.nm_flags.recursion_available &&
+ nmb->header.response)
+ ubuf[offset+3] |= 0x80;
+ if (nmb->header.nm_flags.bcast)
+ ubuf[offset+3] |= 0x10;
+ ubuf[offset+3] |= (nmb->header.rcode & 0xF);
+
+ RSSVAL(ubuf,offset+4,nmb->header.qdcount);
+ RSSVAL(ubuf,offset+6,nmb->header.ancount);
+ RSSVAL(ubuf,offset+8,nmb->header.nscount);
+ RSSVAL(ubuf,offset+10,nmb->header.arcount);
+ }
+
+ offset += 12;
+ if (nmb->header.qdcount) {
+ /* XXXX this doesn't handle a qdcount of > 1 */
+ if (len) {
+ /* Length check. */
+ int extra = put_nmb_name(NULL,0,offset,
+ &nmb->question.question_name);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_nmb_name((char *)ubuf,len,offset,
+ &nmb->question.question_name);
+ if (buf) {
+ RSSVAL(ubuf,offset,nmb->question.question_type);
+ RSSVAL(ubuf,offset+2,nmb->question.question_class);
+ }
+ offset += 4;
+ }
+
+ if (nmb->header.ancount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->answers,
+ nmb->header.ancount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->answers,
+ nmb->header.ancount);
+ }
+
+ if (nmb->header.nscount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->nsrecs,
+ nmb->header.nscount);
+ }
+
+ /*
+ * The spec says we must put compressed name pointers
+ * in the following outgoing packets :
+ * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST,
+ * NAME_RELEASE_REQUEST.
+ */
+
+ if((nmb->header.response == False) &&
+ ((nmb->header.opcode == NMB_NAME_REG_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) ||
+ (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) ||
+ (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) &&
+ (nmb->header.arcount == 1)) {
+
+ if (len) {
+ /* Length check. */
+ int extra = put_compressed_name_ptr(NULL,offset,
+ nmb->additional,12);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_compressed_name_ptr(ubuf,offset,
+ nmb->additional,12);
+ } else if (nmb->header.arcount) {
+ if (len) {
+ /* Length check. */
+ int extra = put_res_rec(NULL,0,offset,nmb->additional,
+ nmb->header.arcount);
+ if (offset + extra > len) {
+ return 0;
+ }
+ }
+ offset += put_res_rec((char *)ubuf,len,offset,nmb->additional,
+ nmb->header.arcount);
+ }
+ return offset;
+}
+
+/*******************************************************************
+ Linearise a packet.
+******************************************************************/
+
+int build_packet(char *buf, size_t buflen, struct packet_struct *p)
+{
+ int len = 0;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ len = build_nmb(buf,buflen,&p->packet.nmb);
+ break;
+
+ case DGRAM_PACKET:
+ len = build_dgram(buf,buflen,&p->packet.dgram);
+ break;
+ }
+
+ return len;
+}
+
+/*******************************************************************
+ Send a packet_struct.
+******************************************************************/
+
+bool send_packet(struct packet_struct *p)
+{
+ char buf[1024];
+ int len=0;
+
+ memset(buf,'\0',sizeof(buf));
+
+ len = build_packet(buf, sizeof(buf), p);
+
+ if (!len)
+ return(False);
+
+ return(send_udp(p->send_fd,buf,len,p->ip,p->port));
+}
+
+/****************************************************************************
+ Receive a UDP/138 packet either via UDP or from the unexpected packet
+ queue. The packet must be a reply packet and have the specified mailslot name
+ The timeout is in milliseconds.
+***************************************************************************/
+
+/****************************************************************************
+ See if a datagram has the right mailslot name.
+***************************************************************************/
+
+bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name)
+{
+ struct dgram_packet *dgram = &p->packet.dgram;
+ char *buf;
+
+ buf = &dgram->data[0];
+ buf -= 4;
+
+ buf = smb_buf(buf);
+
+ if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) {
+ return True;
+ }
+
+ return False;
+}
+
+/****************************************************************************
+ Return the number of bits that match between two len character buffers
+***************************************************************************/
+
+int matching_len_bits(const unsigned char *p1, const unsigned char *p2, size_t len)
+{
+ size_t i, j;
+ int ret = 0;
+ for (i=0; i<len; i++) {
+ if (p1[i] != p2[i])
+ break;
+ ret += 8;
+ }
+
+ if (i==len)
+ return ret;
+
+ for (j=0; j<8; j++) {
+ if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j))))
+ break;
+ ret++;
+ }
+
+ return ret;
+}
+
+static unsigned char sort_ip[4];
+
+/****************************************************************************
+ Compare two query reply records.
+***************************************************************************/
+
+static int name_query_comp(unsigned char *p1, unsigned char *p2)
+{
+ return matching_len_bits(p2+2, sort_ip, 4) -
+ matching_len_bits(p1+2, sort_ip, 4);
+}
+
+/****************************************************************************
+ Sort a set of 6 byte name query response records so that the IPs that
+ have the most leading bits in common with the specified address come first.
+***************************************************************************/
+
+void sort_query_replies(char *data, int n, struct in_addr ip)
+{
+ if (n <= 1)
+ return;
+
+ putip(sort_ip, (char *)&ip);
+
+ /* TODO:
+ this can't use TYPESAFE_QSORT() as the types are wrong.
+ It should be fixed to use a real type instead of char*
+ */
+ qsort(data, n, 6, QSORT_CAST name_query_comp);
+}
+
+/****************************************************************************
+ Interpret the weird netbios "name" into a unix fstring. Return the name type.
+ Returns -1 on error.
+****************************************************************************/
+
+static int name_interpret(unsigned char *buf, size_t buf_len,
+ unsigned char *in, fstring name)
+{
+ unsigned char *end_ptr = buf + buf_len;
+ int ret;
+ unsigned int len;
+ fstring out_string;
+ unsigned char *out = (unsigned char *)out_string;
+
+ *out=0;
+
+ if (in >= end_ptr) {
+ return -1;
+ }
+ len = (*in++) / 2;
+
+ if (len<1) {
+ return -1;
+ }
+
+ while (len--) {
+ if (&in[1] >= end_ptr) {
+ return -1;
+ }
+ if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((in[0]-'A')<<4) + (in[1]-'A');
+ in += 2;
+ out++;
+ if (PTR_DIFF(out,out_string) >= sizeof(fstring)) {
+ return -1;
+ }
+ }
+ ret = out[-1];
+ out[-1] = 0;
+
+ pull_ascii_fstring(name, out_string);
+
+ return(ret);
+}
+
+/****************************************************************************
+ Mangle a name into netbios format.
+ Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum.
+****************************************************************************/
+
+char *name_mangle(TALLOC_CTX *mem_ctx, const char *In, char name_type)
+{
+ int i;
+ int len;
+ nstring buf;
+ char *result;
+ char *p;
+
+ result = talloc_array(mem_ctx, char, 33 + strlen(lp_netbios_scope()) + 2);
+ if (result == NULL) {
+ return NULL;
+ }
+ p = result;
+
+ /* Safely copy the input string, In, into buf[]. */
+ if (strcmp(In,"*") == 0)
+ put_name(buf, "*", '\0', 0x00);
+ else {
+ /* We use an fstring here as mb dos names can expend x3 when
+ going to utf8. */
+ fstring buf_unix;
+ nstring buf_dos;
+
+ pull_ascii_fstring(buf_unix, In);
+ if (!strupper_m(buf_unix)) {
+ return NULL;
+ }
+
+ push_ascii_nstring(buf_dos, buf_unix);
+ put_name(buf, buf_dos, ' ', name_type);
+ }
+
+ /* Place the length of the first field into the output buffer. */
+ p[0] = 32;
+ p++;
+
+ /* Now convert the name to the rfc1001/1002 format. */
+ for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) {
+ p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A';
+ p[(i*2)+1] = (buf[i] & 0x000F) + 'A';
+ }
+ p += 32;
+ p[0] = '\0';
+
+ /* Add the scope string. */
+ for( i = 0, len = 0; *(lp_netbios_scope()) != '\0'; i++, len++ ) {
+ switch( (lp_netbios_scope())[i] ) {
+ case '\0':
+ p[0] = len;
+ if( len > 0 )
+ p[len+1] = 0;
+ return result;
+ case '.':
+ p[0] = len;
+ p += (len + 1);
+ len = -1;
+ break;
+ default:
+ p[len+1] = (lp_netbios_scope())[i];
+ break;
+ }
+ }
+
+ return result;
+}
+
+/****************************************************************************
+ Find a pointer to a netbios name.
+****************************************************************************/
+
+static unsigned char *name_ptr(unsigned char *buf, size_t buf_len, unsigned int ofs)
+{
+ unsigned char c = 0;
+
+ if (ofs > buf_len || buf_len < 1) {
+ return NULL;
+ }
+
+ c = *(unsigned char *)(buf+ofs);
+ if ((c & 0xC0) == 0xC0) {
+ uint16_t l = 0;
+
+ if (ofs > buf_len - 1) {
+ return NULL;
+ }
+ l = RSVAL(buf, ofs) & 0x3FFF;
+ if (l > buf_len) {
+ return NULL;
+ }
+ DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l));
+ return(buf + l);
+ } else {
+ return(buf+ofs);
+ }
+}
+
+/****************************************************************************
+ Extract a netbios name from a buf (into a unix string) return name type.
+ Returns -1 on error.
+****************************************************************************/
+
+int name_extract(unsigned char *buf, size_t buf_len, unsigned int ofs, fstring name)
+{
+ unsigned char *p = name_ptr(buf,buf_len,ofs);
+
+ name[0] = '\0';
+ if (p == NULL) {
+ return -1;
+ }
+ return(name_interpret(buf,buf_len,p,name));
+}
+
+/****************************************************************************
+ Return the total storage length of a mangled name.
+ Returns -1 on error.
+****************************************************************************/
+
+int name_len(unsigned char *s1, size_t buf_len)
+{
+ /* NOTE: this argument _must_ be unsigned */
+ unsigned char *s = (unsigned char *)s1;
+ int len = 0;
+
+ if (buf_len < 1) {
+ return -1;
+ }
+ /* If the two high bits of the byte are set, return 2. */
+ if (0xC0 == (*s & 0xC0)) {
+ if (buf_len < 2) {
+ return -1;
+ }
+ return(2);
+ }
+
+ /* Add up the length bytes. */
+ for (len = 1; (*s); s += (*s) + 1) {
+ len += *s + 1;
+ if (len > buf_len) {
+ return -1;
+ }
+ }
+
+ return(len);
+}
+
+/*******************************************************************
+ Setup the word count and byte count for a client smb message.
+********************************************************************/
+
+int cli_set_message(char *buf,int num_words,int num_bytes,bool zero)
+{
+ if (zero && (num_words || num_bytes)) {
+ memset(buf + smb_size,'\0',num_words*2 + num_bytes);
+ }
+ SCVAL(buf,smb_wct,num_words);
+ SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes);
+ smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4);
+ return (smb_size + num_words*2 + num_bytes);
+}
diff --git a/source3/libsmb/nmblib.h b/source3/libsmb/nmblib.h
new file mode 100644
index 0000000..52600a4
--- /dev/null
+++ b/source3/libsmb/nmblib.h
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle unexpected packets
+ NBT netbios library routines
+ Copyright (C) Andrew Tridgell 1994-1998, 2000
+ Copyright (C) Jeremy Allison 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _LIBSMB_NMBLIB_H_
+#define _LIBSMB_NMBLIB_H_
+
+/* The following definitions come from libsmb/unexpected.c */
+
+#include "nameserv.h"
+
+/* The following definitions come from libsmb/nmblib.c */
+
+void debug_nmb_packet(struct packet_struct *p);
+void put_name(char *dest, const char *name, int pad, unsigned int name_type);
+char *nmb_namestr(const struct nmb_name *n);
+struct packet_struct *copy_packet(struct packet_struct *packet);
+void free_packet(struct packet_struct *packet);
+int packet_trn_id(struct packet_struct *p);
+struct packet_struct *parse_packet(char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port);
+struct packet_struct *parse_packet_talloc(TALLOC_CTX *mem_ctx,
+ char *buf,int length,
+ enum packet_type packet_type,
+ struct in_addr ip,
+ int port);
+void make_nmb_name( struct nmb_name *n, const char *name, int type);
+bool nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2);
+int build_packet(char *buf, size_t buflen, struct packet_struct *p);
+bool send_packet(struct packet_struct *p);
+bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name);
+int matching_len_bits(const unsigned char *p1, const unsigned char *p2, size_t len);
+void sort_query_replies(char *data, int n, struct in_addr ip);
+char *name_mangle(TALLOC_CTX *mem_ctx, const char *In, char name_type);
+int name_extract(unsigned char *buf,size_t buf_len, unsigned int ofs, fstring name);
+int name_len(unsigned char *s1, size_t buf_len);
+int cli_set_message(char *buf,int num_words,int num_bytes,bool zero);
+
+#endif /* _LIBSMB_NMBLIB_H_ */
diff --git a/source3/libsmb/passchange.c b/source3/libsmb/passchange.c
new file mode 100644
index 0000000..b3304ed
--- /dev/null
+++ b/source3/libsmb/passchange.c
@@ -0,0 +1,323 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB client password change routine
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../librpc/gen_ndr/ndr_samr.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_samr.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "libsmb/nmblib.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/*************************************************************
+ Change a password on a remote machine using IPC calls.
+*************************************************************/
+
+NTSTATUS remote_password_change(const char *remote_machine,
+ const char *domain, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ char **err_str)
+{
+ struct cli_state *cli = NULL;
+ struct cli_credentials *creds = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS status;
+ NTSTATUS result;
+ bool pass_must_change = False;
+
+ *err_str = NULL;
+
+ result = cli_connect_nb(remote_machine, NULL, 0, 0x20, NULL,
+ SMB_SIGNING_IPC_DEFAULT, 0, &cli);
+ if (!NT_STATUS_IS_OK(result)) {
+ if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) {
+ if (asprintf(err_str, "Unable to connect to SMB server on "
+ "machine %s. NetBIOS support disabled\n",
+ remote_machine) == -1) {
+ *err_str = NULL;
+ }
+ } else {
+ if (asprintf(err_str, "Unable to connect to SMB server on "
+ "machine %s. Error was : %s.\n",
+ remote_machine, nt_errstr(result))==-1) {
+ *err_str = NULL;
+ }
+ }
+ return result;
+ }
+
+ creds = cli_session_creds_init(cli,
+ user_name,
+ domain,
+ NULL, /* realm */
+ old_passwd,
+ false, /* use_kerberos */
+ false, /* fallback_after_kerberos */
+ false, /* use_ccache */
+ false); /* password_is_nt_hash */
+ SMB_ASSERT(creds != NULL);
+
+ result = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ lp_client_ipc_min_protocol(),
+ lp_client_ipc_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (asprintf(err_str, "machine %s rejected the negotiate "
+ "protocol. Error was : %s.\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Given things like SMB signing, restrict anonymous and the like,
+ try an authenticated connection first */
+ result = cli_session_setup_creds(cli, creds);
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Password must change or Password expired are the only valid
+ * error conditions here from where we can proceed, the rest like
+ * account locked out or logon failure will lead to errors later
+ * anyway */
+
+ if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) {
+ if (asprintf(err_str, "Could not connect to machine %s: "
+ "%s\n", remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+
+ pass_must_change = True;
+
+ /*
+ * We should connect as the anonymous user here, in case
+ * the server has "must change password" checked...
+ * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix.
+ */
+
+ result = cli_session_setup_anon(cli);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (asprintf(err_str, "machine %s rejected the session "
+ "setup. Error was : %s.\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+ }
+
+ result = cli_tree_connect(cli, "IPC$", "IPC", NULL);
+ if (!NT_STATUS_IS_OK(result)) {
+ if (asprintf(err_str, "machine %s rejected the tconX on the "
+ "IPC$ share. Error was : %s.\n",
+ remote_machine, nt_errstr(result))) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* Try not to give the password away too easily */
+
+ if (!pass_must_change) {
+ const struct sockaddr_storage *remote_sockaddr =
+ smbXcli_conn_remote_sockaddr(cli->conn);
+
+ result = cli_rpc_pipe_open_with_creds(cli,
+ &ndr_table_samr,
+ NCACN_NP,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ remote_machine,
+ remote_sockaddr,
+ creds,
+ &pipe_hnd);
+ } else {
+ /*
+ * If the user password must be changed the ntlmssp bind will
+ * fail the same way as the session setup above did. The
+ * difference is that with a pipe bind we don't get a good
+ * error message, the result will be that the rpc call below
+ * will just fail. So we do it anonymously, there's no other
+ * way.
+ */
+ result = cli_rpc_pipe_open_noauth(
+ cli, &ndr_table_samr, &pipe_hnd);
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ if (lp_client_lanman_auth()) {
+ /* Use the old RAP method. */
+ if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
+ result = cli_nt_error(cli);
+ if (asprintf(err_str, "machine %s rejected the "
+ "password change: Error was : %s.\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+ } else {
+ if (asprintf(err_str, "SAMR connection to machine %s "
+ "failed. Error was %s, but LANMAN password "
+ "changes are disabled\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+ }
+
+ status = dcerpc_samr_chgpasswd_user4(pipe_hnd->binding_handle,
+ talloc_tos(),
+ pipe_hnd->srv_name_slash,
+ user_name,
+ old_passwd,
+ new_passwd,
+ &result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) {
+ /* All good, password successfully changed. */
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ /* DO NOT FALLBACK TO RC4 */
+ if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) {
+ cli_shutdown(cli);
+ return NT_STATUS_STRONG_CRYPTO_NOT_SUPPORTED;
+ }
+ }
+ } else {
+ if (!NT_STATUS_IS_OK(result)) {
+ int rc = asprintf(
+ err_str,
+ "machine %s rejected to change the password"
+ "with error: %s",
+ remote_machine,
+ get_friendly_nt_error_msg(result));
+ if (rc <= 0) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+ }
+
+ result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(),
+ user_name, new_passwd, old_passwd);
+ if (NT_STATUS_IS_OK(result)) {
+ /* Great - it all worked! */
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+
+ } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
+ /* it failed, but for reasons such as wrong password, too short etc ... */
+
+ if (asprintf(err_str, "machine %s rejected the password change: "
+ "Error was : %s.\n",
+ remote_machine, get_friendly_nt_error_msg(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* OK, that failed, so try again... */
+ TALLOC_FREE(pipe_hnd);
+
+ /* Try anonymous NTLMSSP... */
+ result = NT_STATUS_UNSUCCESSFUL;
+
+ /* OK, this is ugly, but... try an anonymous pipe. */
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
+ &pipe_hnd);
+
+ if ( NT_STATUS_IS_OK(result) &&
+ (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2(
+ pipe_hnd, talloc_tos(), user_name,
+ new_passwd, old_passwd)))) {
+ /* Great - it all worked! */
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ } else {
+ if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED)
+ || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) {
+ /* it failed, but again it was due to things like new password too short */
+
+ if (asprintf(err_str, "machine %s rejected the "
+ "(anonymous) password change: Error was : "
+ "%s.\n", remote_machine,
+ get_friendly_nt_error_msg(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ }
+
+ /* We have failed to change the user's password, and we think the server
+ just might not support SAMR password changes, so fall back */
+
+ if (lp_client_lanman_auth()) {
+ /* Use the old RAP method. */
+ if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) {
+ /* SAMR failed, but the old LanMan protocol worked! */
+
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ }
+
+ result = cli_nt_error(cli);
+ if (asprintf(err_str, "machine %s rejected the password "
+ "change: Error was : %s.\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return result;
+ } else {
+ if (asprintf(err_str, "SAMR connection to machine %s "
+ "failed. Error was %s, but LANMAN password "
+ "changes are disabled\n",
+ remote_machine, nt_errstr(result)) == -1) {
+ *err_str = NULL;
+ }
+ cli_shutdown(cli);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+}
diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
new file mode 100644
index 0000000..35b6577
--- /dev/null
+++ b/source3/libsmb/proto.h
@@ -0,0 +1,1080 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Bartlett 2001-2003
+ Copyright (C) Andrew Tridgell 1994-1998,2000-2001
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) Jelmer Vernooij 2003
+ Copyright (C) Jeremy Allison 2001-2009,2011
+ Copyright (C) Stefan Metzmacher 2003,2009
+ Copyright (C) Volker Lendecke 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIBSMB_PROTO_H_
+#define _LIBSMB_PROTO_H_
+
+struct smb_trans_enc_state;
+struct cli_credentials;
+struct cli_state;
+struct file_info;
+struct print_job_info;
+
+/* The following definitions come from libsmb/cliconnect.c */
+
+struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx,
+ const char *username,
+ const char *domain,
+ const char *realm,
+ const char *password,
+ bool use_kerberos,
+ bool fallback_after_kerberos,
+ bool use_ccache,
+ bool password_is_nt_hash);
+NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli,
+ struct cli_credentials *creds);
+struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct cli_credentials *creds);
+NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req);
+NTSTATUS cli_session_setup_creds(struct cli_state *cli,
+ struct cli_credentials *creds);
+NTSTATUS cli_session_setup_anon(struct cli_state *cli);
+struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req);
+NTSTATUS cli_ulogoff(struct cli_state *cli);
+struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *share, const char *dev,
+ const char *pass, int passlen,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *share, const char *dev,
+ const char *pass, int passlen);
+NTSTATUS cli_tcon_andx_recv(struct tevent_req *req);
+NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass, int passlen);
+NTSTATUS cli_tree_connect_creds(struct cli_state *cli,
+ const char *share, const char *dev,
+ struct cli_credentials *creds);
+NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share,
+ const char *dev, const char *pass);
+NTSTATUS cli_tdis(struct cli_state *cli);
+NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss,
+ uint16_t port, int name_type, const char *myname,
+ enum smb_signing_setting signing_state, int flags, struct cli_state **pcli);
+NTSTATUS cli_start_connection(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ enum smb_signing_setting signing_state, int flags);
+NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli,
+ struct cli_credentials *creds);
+
+struct smb2_negotiate_contexts;
+struct tevent_req *cli_full_connection_creds_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ const char *my_name, const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ struct cli_credentials *creds,
+ int flags,
+ struct smb2_negotiate_contexts *negotiate_contexts);
+NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req,
+ struct cli_state **output_cli);
+NTSTATUS cli_full_connection_creds(struct cli_state **output_cli,
+ const char *my_name,
+ const char *dest_host,
+ const struct sockaddr_storage *dest_ss, int port,
+ const char *service, const char *service_type,
+ struct cli_credentials *creds,
+ int flags);
+NTSTATUS cli_raw_tcon(struct cli_state *cli,
+ const char *service, const char *pass, const char *dev,
+ uint16_t *max_xmit, uint16_t *tid);
+struct cli_state *get_ipc_connect(char *server,
+ struct sockaddr_storage *server_ss,
+ struct cli_credentials *creds);
+struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx,
+ struct sockaddr_storage *mb_ip,
+ struct cli_credentials *creds,
+ char **pp_workgroup_out);
+
+/* The following definitions come from libsmb/clidfs.c */
+
+NTSTATUS cli_cm_open(TALLOC_CTX *ctx,
+ struct cli_state *referring_cli,
+ const char *server,
+ const char *share,
+ struct cli_credentials *creds,
+ const struct sockaddr_storage *dest_ss,
+ int port,
+ int name_type,
+ struct cli_state **pcli);
+void cli_cm_display(struct cli_state *c);
+struct client_dfs_referral;
+bool cli_dfs_is_already_full_path(struct cli_state *cli, const char *path);
+NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path,
+ uint16_t max_referral_level,
+ struct client_dfs_referral **refs,
+ size_t *num_refs,
+ size_t *consumed);
+NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path,
+ struct client_dfs_referral **refs,
+ size_t *num_refs,
+ size_t *consumed);
+NTSTATUS cli_resolve_path(TALLOC_CTX *ctx,
+ const char *mountpt,
+ struct cli_credentials *creds,
+ struct cli_state *rootcli,
+ const char *path,
+ struct cli_state **targetcli,
+ char **pp_targetpath);
+
+bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *sharename,
+ char **pp_newserver,
+ char **pp_newshare,
+ struct cli_credentials *creds);
+
+NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ const char *fname_dst,
+ const char **fname_dst_out);
+char *smb1_dfs_share_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *path);
+
+/* The following definitions come from libsmb/clientgen.c */
+
+unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout);
+bool cli_set_backup_intent(struct cli_state *cli, bool flag);
+extern struct GUID cli_state_client_guid;
+struct cli_state *cli_state_create(TALLOC_CTX *mem_ctx,
+ int fd,
+ const char *remote_name,
+ enum smb_signing_setting signing_state,
+ int flags);
+void cli_shutdown(struct cli_state *cli);
+uint16_t cli_state_get_vc_num(struct cli_state *cli);
+uint32_t cli_setpid(struct cli_state *cli, uint32_t pid);
+uint32_t cli_getpid(struct cli_state *cli);
+bool cli_state_is_encryption_on(struct cli_state *cli);
+bool cli_state_has_tcon(struct cli_state *cli);
+uint32_t cli_state_get_tid(struct cli_state *cli);
+uint32_t cli_state_set_tid(struct cli_state *cli, uint32_t tid);
+struct smbXcli_tcon;
+void cli_state_save_tcon_share(struct cli_state *cli,
+ struct smbXcli_tcon **_tcon_ret,
+ char **_share_ret);
+void cli_state_restore_tcon_share(struct cli_state *cli,
+ struct smbXcli_tcon *tcon,
+ char *share);
+uint16_t cli_state_get_uid(struct cli_state *cli);
+uint16_t cli_state_set_uid(struct cli_state *cli, uint16_t uid);
+bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive);
+uint32_t cli_state_available_size(struct cli_state *cli, uint32_t ofs);
+time_t cli_state_server_time(struct cli_state *cli);
+struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, uint16_t num_echos,
+ DATA_BLOB data);
+NTSTATUS cli_echo_recv(struct tevent_req *req);
+NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data);
+NTSTATUS cli_smb(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint8_t smb_command, uint8_t additional_flags,
+ uint8_t wct, uint16_t *vwv,
+ uint32_t num_bytes, const uint8_t *bytes,
+ struct tevent_req **result_parent,
+ uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
+ uint32_t *pnum_bytes, uint8_t **pbytes);
+
+/* The following definitions come from libsmb/clierror.c */
+
+NTSTATUS cli_nt_error(struct cli_state *cli);
+int cli_status_to_errno(NTSTATUS status);
+int cli_errno(struct cli_state *cli);
+bool cli_is_error(struct cli_state *cli);
+bool cli_state_is_connected(struct cli_state *cli);
+
+/* The following definitions come from libsmb/clifile.c */
+
+struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t level,
+ const char *path,
+ uint8_t *data,
+ size_t data_len);
+NTSTATUS cli_setpathinfo_recv(struct tevent_req *req);
+NTSTATUS cli_setpathinfo(struct cli_state *cli,
+ uint16_t level,
+ const char *path,
+ uint8_t *data,
+ size_t data_len);
+struct tevent_req *cli_setfileinfo_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t level,
+ uint8_t *data,
+ size_t data_len);
+NTSTATUS cli_setfileinfo_recv(struct tevent_req *req);
+
+struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *oldname,
+ const char *newname);
+NTSTATUS cli_posix_symlink_recv(struct tevent_req *req);
+NTSTATUS cli_posix_symlink(struct cli_state *cli,
+ const char *oldname,
+ const char *newname);
+struct tevent_req *cli_posix_readlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_posix_readlink_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, char **target);
+struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *oldname,
+ const char *newname);
+NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req);
+NTSTATUS cli_posix_hardlink(struct cli_state *cli,
+ const char *oldname,
+ const char *newname);
+uint32_t unix_perms_to_wire(mode_t perms);
+mode_t wire_perms_to_unix(uint32_t perms);
+struct tevent_req *cli_posix_getacl_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_posix_getacl_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *prb_size,
+ char **retbuf);
+NTSTATUS cli_posix_getacl(struct cli_state *cli,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ size_t *prb_size,
+ char **retbuf);
+struct tevent_req *cli_posix_setacl_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ const void *acl_buf,
+ size_t acl_buf_size);
+NTSTATUS cli_posix_setacl_recv(struct tevent_req *req);
+NTSTATUS cli_posix_setacl(struct cli_state *cli,
+ const char *fname,
+ const void *acl_buf,
+ size_t acl_buf_size);
+struct tevent_req *cli_posix_stat_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_posix_stat_recv(struct tevent_req *req, struct stat_ex *sbuf);
+NTSTATUS cli_posix_stat(struct cli_state *cli,
+ const char *fname,
+ struct stat_ex *sbuf);
+struct tevent_req *cli_posix_chmod_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode);
+NTSTATUS cli_posix_chmod_recv(struct tevent_req *req);
+NTSTATUS cli_posix_chmod(struct cli_state *cli, const char *fname, mode_t mode);
+struct tevent_req *cli_posix_chown_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uid_t uid,
+ gid_t gid);
+NTSTATUS cli_posix_chown_recv(struct tevent_req *req);
+NTSTATUS cli_posix_chown(struct cli_state *cli,
+ const char *fname,
+ uid_t uid,
+ gid_t gid);
+struct tevent_req *cli_mknod_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode,
+ dev_t dev);
+NTSTATUS cli_mknod_recv(struct tevent_req *req);
+NTSTATUS
+cli_mknod(struct cli_state *cli, const char *fname, mode_t mode, dev_t dev);
+struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace);
+NTSTATUS cli_rename_recv(struct tevent_req *req);
+NTSTATUS cli_rename(struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace);
+struct tevent_req *cli_ntrename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst);
+NTSTATUS cli_ntrename_recv(struct tevent_req *req);
+NTSTATUS cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst);
+
+struct tevent_req *cli_hardlink_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst);
+NTSTATUS cli_hardlink_recv(struct tevent_req *req);
+NTSTATUS cli_hardlink(
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst);
+
+struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t mayhave_attrs);
+NTSTATUS cli_unlink_recv(struct tevent_req *req);
+NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint32_t mayhave_attrs);
+
+struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname);
+NTSTATUS cli_mkdir_recv(struct tevent_req *req);
+NTSTATUS cli_mkdir(struct cli_state *cli, const char *dname);
+struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *dname);
+NTSTATUS cli_rmdir_recv(struct tevent_req *req);
+NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname);
+struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool flag);
+NTSTATUS cli_nt_delete_on_close_recv(struct tevent_req *req);
+NTSTATUS cli_nt_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag);
+struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint32_t ImpersonationLevel,
+ uint8_t SecurityFlags);
+NTSTATUS cli_ntcreate_recv(struct tevent_req *req,
+ uint16_t *pfnum,
+ struct smb_create_returns *cr);
+NTSTATUS cli_ntcreate(struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ uint16_t *pfid,
+ struct smb_create_returns *cr);
+struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ int flags, int share_mode,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_openx_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, const char *fname,
+ int flags, int share_mode);
+NTSTATUS cli_openx_recv(struct tevent_req *req, uint16_t *fnum);
+NTSTATUS cli_openx(struct cli_state *cli, const char *fname, int flags, int share_mode, uint16_t *pfnum);
+NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode, uint16_t *pfnum);
+struct tevent_req *cli_smb1_close_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ struct tevent_req **psubreq);
+struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t flags);
+NTSTATUS cli_close_recv(struct tevent_req *req);
+NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum);
+struct tevent_req *cli_ftruncate_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t size);
+NTSTATUS cli_ftruncate_recv(struct tevent_req *req);
+NTSTATUS cli_ftruncate(struct cli_state *cli, uint16_t fnum, uint64_t size);
+NTSTATUS cli_locktype(struct cli_state *cli, uint16_t fnum,
+ uint32_t offset, uint32_t len,
+ int timeout, unsigned char locktype);
+struct smb1_lock_element {
+ uint16_t pid;
+ uint64_t offset;
+ uint64_t length;
+};
+
+struct tevent_req *cli_lockingx_create(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_lockingx_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks);
+NTSTATUS cli_lockingx_recv(struct tevent_req *req);
+NTSTATUS cli_lockingx(
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint8_t typeoflock,
+ uint8_t newoplocklevel,
+ int32_t timeout,
+ uint16_t num_unlocks,
+ const struct smb1_lock_element *unlocks,
+ uint16_t num_locks,
+ const struct smb1_lock_element *locks);
+
+NTSTATUS cli_lock32(struct cli_state *cli, uint16_t fnum, uint32_t offset,
+ uint32_t len, int timeout, enum brl_type lock_type);
+struct tevent_req *cli_unlock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len);
+NTSTATUS cli_unlock_recv(struct tevent_req *req);
+NTSTATUS cli_unlock(struct cli_state *cli, uint16_t fnum, uint32_t offset, uint32_t len);
+struct tevent_req *cli_posix_lock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len,
+ bool wait_lock,
+ enum brl_type lock_type);
+NTSTATUS cli_posix_lock_recv(struct tevent_req *req);
+NTSTATUS cli_posix_lock(struct cli_state *cli, uint16_t fnum,
+ uint64_t offset, uint64_t len,
+ bool wait_lock, enum brl_type lock_type);
+struct tevent_req *cli_posix_unlock_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint64_t offset,
+ uint64_t len);
+NTSTATUS cli_posix_unlock_recv(struct tevent_req *req);
+NTSTATUS cli_posix_unlock(struct cli_state *cli, uint16_t fnum, uint64_t offset, uint64_t len);
+struct tevent_req *cli_getattrE_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum);
+NTSTATUS cli_getattrE_recv(struct tevent_req *req,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time);
+struct tevent_req *cli_setattrE_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time);
+NTSTATUS cli_setattrE_recv(struct tevent_req *req);
+NTSTATUS cli_setattrE(struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time);
+struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_getatr_recv(struct tevent_req *req,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *write_time);
+NTSTATUS cli_getatr(struct cli_state *cli,
+ const char *fname,
+ uint32_t *pattr,
+ off_t *size,
+ time_t *write_time);
+struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t attr,
+ time_t mtime);
+NTSTATUS cli_setatr_recv(struct tevent_req *req);
+NTSTATUS cli_setatr(struct cli_state *cli,
+ const char *fname,
+ uint32_t attr,
+ time_t mtime);
+struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_chkpath_recv(struct tevent_req *req);
+NTSTATUS cli_chkpath(struct cli_state *cli, const char *path);
+struct tevent_req *cli_dskattr_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_dskattr_recv(struct tevent_req *req, int *bsize, int *total,
+ int *avail);
+NTSTATUS cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail);
+NTSTATUS cli_disk_size(struct cli_state *cli, const char *path, uint64_t *bsize,
+ uint64_t *total, uint64_t *avail);
+struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *path);
+NTSTATUS cli_ctemp_recv(struct tevent_req *req,
+ TALLOC_CTX *ctx,
+ uint16_t *pfnum,
+ char **outfile);
+NTSTATUS cli_ctemp(struct cli_state *cli,
+ TALLOC_CTX *ctx,
+ const char *path,
+ uint16_t *pfnum,
+ char **out_path);
+NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path,
+ const char *ea_name, const char *ea_val,
+ size_t ea_len);
+NTSTATUS cli_set_ea_fnum(struct cli_state *cli, uint16_t fnum,
+ const char *ea_name, const char *ea_val,
+ size_t ea_len);
+struct tevent_req *cli_get_ea_list_path_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_get_ea_list_path_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ size_t *pnum_eas, struct ea_struct **peas);
+NTSTATUS cli_get_ea_list_path(struct cli_state *cli, const char *path,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_list);
+struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ int flags,
+ mode_t mode);
+NTSTATUS cli_posix_open_recv(struct tevent_req *req, uint16_t *pfnum);
+NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname,
+ int flags, mode_t mode, uint16_t *fnum);
+struct tevent_req *cli_posix_mkdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ mode_t mode);
+NTSTATUS cli_posix_mkdir_recv(struct tevent_req *req);
+NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode);
+
+struct tevent_req *cli_posix_unlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_posix_unlink_recv(struct tevent_req *req);
+NTSTATUS cli_posix_unlink(struct cli_state *cli, const char *fname);
+
+struct tevent_req *cli_posix_rmdir_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_posix_rmdir_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx);
+NTSTATUS cli_posix_rmdir(struct cli_state *cli, const char *fname);
+struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint32_t buffer_size,
+ uint32_t completion_filter, bool recursive);
+NTSTATUS cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint32_t *pnum_changes,
+ struct notify_change **pchanges);
+NTSTATUS cli_notify(struct cli_state *cli, uint16_t fnum, uint32_t buffer_size,
+ uint32_t completion_filter, bool recursive,
+ TALLOC_CTX *mem_ctx, uint32_t *pnum_changes,
+ struct notify_change **pchanges);
+
+struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ struct security_descriptor *secdesc,
+ struct ea_struct *eas,
+ int num_eas);
+NTSTATUS cli_nttrans_create_recv(struct tevent_req *req,
+ uint16_t *fnum,
+ struct smb_create_returns *cr);
+NTSTATUS cli_nttrans_create(struct cli_state *cli,
+ const char *fname,
+ uint32_t CreatFlags,
+ uint32_t DesiredAccess,
+ uint32_t FileAttributes,
+ uint32_t ShareAccess,
+ uint32_t CreateDisposition,
+ uint32_t CreateOptions,
+ uint8_t SecurityFlags,
+ struct security_descriptor *secdesc,
+ struct ea_struct *eas,
+ int num_eas,
+ uint16_t *pfid,
+ struct smb_create_returns *cr);
+
+/* The following definitions come from libsmb/clifsinfo.c */
+
+struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req,
+ uint16_t *pmajor, uint16_t *pminor,
+ uint32_t *pcaplow,
+ uint32_t *pcaphigh);
+NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16_t *pmajor,
+ uint16_t *pminor, uint32_t *pcaplow,
+ uint32_t *pcaphigh);
+struct tevent_req *cli_set_unix_extensions_capabilities_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli,
+ uint16_t major, uint16_t minor, uint32_t caplow, uint32_t caphigh);
+NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req);
+NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli,
+ uint16_t major, uint16_t minor,
+ uint32_t caplow, uint32_t caphigh);
+struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr);
+NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr);
+NTSTATUS cli_get_fs_volume_info(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx, char **volume_name,
+ uint32_t *pserial_number, time_t *pdate);
+NTSTATUS cli_get_fs_full_size_info(struct cli_state *cli,
+ uint64_t *total_allocation_units,
+ uint64_t *caller_allocation_units,
+ uint64_t *actual_allocation_units,
+ uint64_t *sectors_per_allocation_unit,
+ uint64_t *bytes_per_sector);
+NTSTATUS cli_get_posix_fs_info(struct cli_state *cli,
+ uint32_t *optimal_transfer_size,
+ uint32_t *block_size,
+ uint64_t *total_blocks,
+ uint64_t *blocks_available,
+ uint64_t *user_blocks_available,
+ uint64_t *total_file_nodes,
+ uint64_t *free_file_nodes,
+ uint64_t *fs_identifier);
+struct tevent_req *cli_posix_whoami_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_posix_whoami_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint64_t *puid,
+ uint64_t *pgid,
+ uint32_t *pnum_gids,
+ uint64_t **pgids,
+ uint32_t *pnum_sids,
+ struct dom_sid **psids,
+ bool *pguest);
+NTSTATUS cli_posix_whoami(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ uint64_t *puid,
+ uint64_t *pgid,
+ uint32_t *num_gids,
+ uint64_t **gids,
+ uint32_t *num_sids,
+ struct dom_sid **sids,
+ bool *pguest);
+
+/* The following definitions come from libsmb/clilist.c */
+
+NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
+ const struct file_info *finfo);
+
+NTSTATUS cli_list_old(struct cli_state *cli,const char *Mask,uint32_t attribute,
+ NTSTATUS (*fn)(struct file_info *,
+ const char *, void *), void *state);
+NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
+ uint32_t attribute, int info_level,
+ NTSTATUS (*fn)(
+ struct file_info *finfo,
+ const char *mask,
+ void *private_data),
+ void *private_data);
+struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute,
+ uint16_t info_level);
+NTSTATUS cli_list_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo);
+NTSTATUS cli_list(struct cli_state *cli,
+ const char *mask,
+ uint32_t attribute,
+ NTSTATUS (*fn)(struct file_info *finfo,
+ const char *mask,
+ void *private_data),
+ void *private_data);
+
+/* The following definitions come from libsmb/climessage.c */
+
+struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *host, const char *username,
+ const char *message);
+NTSTATUS cli_message_recv(struct tevent_req *req);
+NTSTATUS cli_message(struct cli_state *cli, const char *host,
+ const char *username, const char *message);
+
+/* The following definitions come from libsmb/clioplock.c */
+
+struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli);
+NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req,
+ uint16_t *pfnum,
+ uint8_t *plevel);
+
+struct tevent_req *cli_oplock_ack_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, uint8_t level);
+NTSTATUS cli_oplock_ack_recv(struct tevent_req *req);
+
+/* The following definitions come from libsmb/cliprint.c */
+
+NTSTATUS cli_print_queue(struct cli_state *cli,
+ void (*fn)(struct print_job_info *));
+NTSTATUS cli_printjob_del(struct cli_state *cli, int job);
+
+/* The following definitions come from libsmb/cliquota.c */
+
+NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum);
+void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list);
+bool parse_user_quota_record(const uint8_t *rdata,
+ unsigned int rdata_count,
+ unsigned int *offset,
+ SMB_NTQUOTA_STRUCT *pqt);
+bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_STRUCT *pqt,
+ SMB_NTQUOTA_LIST **pqt_list);
+NTSTATUS parse_user_quota_list(const uint8_t *curdata,
+ uint32_t curdata_size,
+ TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST **pqt_list);
+NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
+ unsigned int rdata_count,
+ SMB_NTQUOTA_STRUCT *pqt);
+NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
+ uint32_t maxlen,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *outbuf,
+ SMB_NTQUOTA_LIST **end_ptr);
+NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
+ const SMB_NTQUOTA_STRUCT *pqt,
+ DATA_BLOB *blob,
+ uint32_t maxlen);
+NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+NTSTATUS cli_set_user_quota(struct cli_state *cli,
+ int quota_fnum,
+ SMB_NTQUOTA_LIST *qtl);
+NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_LIST **pqt_list);
+NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
+ SMB_NTQUOTA_STRUCT *pqt);
+
+/* The following definitions come from libsmb/clireadwrite.c */
+
+struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ off_t offset, size_t size,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ off_t offset, size_t size);
+NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received,
+ uint8_t **rcvbuf);
+struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, off_t start_offset,
+ off_t size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n,
+ void *priv),
+ void *priv);
+NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received);
+NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
+ off_t start_offset, off_t size, size_t window_size,
+ NTSTATUS (*sink)(char *buf, size_t n, void *priv),
+ void *priv, off_t *received);
+NTSTATUS cli_read_sink(char *buf, size_t n, void *priv);
+struct tevent_req *cli_read_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ char *buf,
+ off_t offset,
+ size_t size);
+NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received);
+NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum,
+ char *buf, off_t offset, size_t size,
+ size_t *nread);
+NTSTATUS cli_smbwrite(struct cli_state *cli, uint16_t fnum, char *buf,
+ off_t offset, size_t size1, size_t *ptotal);
+struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size,
+ struct tevent_req **reqs_before,
+ int num_reqs_before,
+ struct tevent_req **psmbreq);
+struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size);
+NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten);
+
+struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli, uint16_t fnum,
+ uint16_t mode, const uint8_t *buf,
+ off_t offset, size_t size);
+NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten);
+
+struct tevent_req *cli_writeall_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size);
+NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten);
+NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
+ const uint8_t *buf, off_t offset, size_t size,
+ size_t *pwritten);
+
+struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum, uint16_t mode,
+ off_t start_offset, size_t window_size,
+ size_t (*source)(uint8_t *buf, size_t n,
+ void *priv),
+ void *priv);
+NTSTATUS cli_push_recv(struct tevent_req *req);
+NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
+ off_t start_offset, size_t window_size,
+ size_t (*source)(uint8_t *buf, size_t n, void *priv),
+ void *priv);
+
+NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size,
+ off_t src_offset, off_t dst_offset,
+ off_t *written,
+ int (*splice_cb)(off_t n, void *priv), void *priv);
+
+/* The following definitions come from libsmb/clisecdesc.c */
+
+struct tevent_req *cli_query_security_descriptor_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info);
+NTSTATUS cli_query_security_descriptor_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd);
+NTSTATUS cli_query_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd);
+NTSTATUS cli_query_secdesc(struct cli_state *cli, uint16_t fnum,
+ TALLOC_CTX *mem_ctx, struct security_descriptor **sd);
+struct tevent_req *cli_set_security_descriptor_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd);
+NTSTATUS cli_set_security_descriptor_recv(struct tevent_req *req);
+NTSTATUS cli_set_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd);
+NTSTATUS cli_set_secdesc(struct cli_state *cli, uint16_t fnum,
+ const struct security_descriptor *sd);
+
+NTSTATUS cli_query_mxac(struct cli_state *cli,
+ const char *filename,
+ uint32_t *mxac);
+
+/* The following definitions come from libsmb/clitrans.c */
+
+struct tevent_req *cli_trans_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct cli_state *cli, uint16_t additional_flags2, uint8_t cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function, int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data);
+NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint16_t *recv_flags2,
+ uint16_t **setup, uint8_t min_setup,
+ uint8_t *num_setup,
+ uint8_t **param, uint32_t min_param,
+ uint32_t *num_param,
+ uint8_t **data, uint32_t min_data,
+ uint32_t *num_data);
+NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ uint8_t trans_cmd,
+ const char *pipe_name, uint16_t fid, uint16_t function,
+ int flags,
+ uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
+ uint8_t *param, uint32_t num_param, uint32_t max_param,
+ uint8_t *data, uint32_t num_data, uint32_t max_data,
+ uint16_t *recv_flags2,
+ uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup,
+ uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam,
+ uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata);
+
+/* The following definitions come from libsmb/clisymlink.c */
+
+struct tevent_req *cli_create_reparse_point_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ DATA_BLOB reparse_blob);
+NTSTATUS cli_create_reparse_point_recv(struct tevent_req *req);
+
+struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *oldpath,
+ const char *newpath,
+ uint32_t flags);
+NTSTATUS cli_symlink_recv(struct tevent_req *req);
+NTSTATUS cli_symlink(struct cli_state *cli, const char *oldname,
+ const char *newname, uint32_t flags);
+
+struct tevent_req *cli_get_reparse_data_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_get_reparse_data_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_data,
+ uint32_t *_datalen);
+NTSTATUS cli_get_reparse_data(struct cli_state *cli,
+ const char *fname,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **_data,
+ uint32_t *_datalen);
+
+struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname);
+NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ char **psubstitute_name, char **pprint_name,
+ uint32_t *pflags);
+NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
+ TALLOC_CTX *mem_ctx, char **psubstitute_name,
+ char **pprint_name, uint32_t *pflags);
+
+NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST *tmp_list,
+ bool return_single,
+ uint32_t max_data,
+ DATA_BLOB *blob,
+ SMB_NTQUOTA_LIST **end_ptr);
+/* The following definitions come from libsmb/passchange.c */
+
+NTSTATUS remote_password_change(const char *remote_machine,
+ const char *domain, const char *user_name,
+ const char *old_passwd, const char *new_passwd,
+ char **err_str);
+
+#endif /* _LIBSMB_PROTO_H_ */
diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c
new file mode 100644
index 0000000..52ba449
--- /dev/null
+++ b/source3/libsmb/pylibsmb.c
@@ -0,0 +1,3030 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * SMB client Python bindings used internally by Samba (for things like
+ * samba-tool). These Python bindings may change without warning, and so
+ * should not be used outside of the Samba codebase.
+ *
+ * Copyright (C) Volker Lendecke 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Template code to use this library:
+
+-------------------------
+from samba.samba3 import libsmb_samba_internal as libsmb
+from samba.samba3 import param as s3param
+from samba import (credentials,NTSTATUSError)
+
+lp = s3param.get_context()
+lp.load("/etc/samba/smb.conf");
+
+creds = credentials.Credentials()
+creds.guess(lp)
+creds.set_username("administrator")
+creds.set_password("1234")
+
+c = libsmb.Conn("127.0.0.1",
+ "tmp",
+ lp,
+ creds,
+ multi_threaded=True)
+-------------------------
+*/
+
+#include "lib/replace/system/python.h"
+#include "includes.h"
+#include "python/py3compat.h"
+#include "python/modules.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "libcli/smb/smb2_negotiate_context.h"
+#include "libcli/smb/reparse.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/security.h"
+#include "system/select.h"
+#include "source4/libcli/util/pyerrors.h"
+#include "auth/credentials/pycredentials.h"
+#include "trans2.h"
+#include "libsmb/clirap.h"
+#include "librpc/rpc/pyrpc_util.h"
+
+#define LIST_ATTRIBUTE_MASK \
+ (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN)
+
+static PyTypeObject *dom_sid_Type = NULL;
+
+static PyTypeObject *get_pytype(const char *module, const char *type)
+{
+ PyObject *mod;
+ PyTypeObject *result;
+
+ mod = PyImport_ImportModule(module);
+ if (mod == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Unable to import %s to check type %s",
+ module, type);
+ return NULL;
+ }
+ result = (PyTypeObject *)PyObject_GetAttrString(mod, type);
+ Py_DECREF(mod);
+ if (result == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Unable to find type %s in module %s",
+ module, type);
+ return NULL;
+ }
+ return result;
+}
+
+/*
+ * We're using "const char * const *" for keywords,
+ * PyArg_ParseTupleAndKeywords expects a "char **". Confine the
+ * inevitable warnings to just one place.
+ */
+static int ParseTupleAndKeywords(PyObject *args, PyObject *kw,
+ const char *format, const char * const *keywords,
+ ...)
+{
+ char **_keywords = discard_const_p(char *, keywords);
+ va_list a;
+ int ret;
+ va_start(a, keywords);
+ ret = PyArg_VaParseTupleAndKeywords(args, kw, format,
+ _keywords, a);
+ va_end(a);
+ return ret;
+}
+
+struct py_cli_thread;
+
+struct py_cli_oplock_break {
+ uint16_t fnum;
+ uint8_t level;
+};
+
+struct py_cli_state {
+ PyObject_HEAD
+ struct cli_state *cli;
+ struct tevent_context *ev;
+ int (*req_wait_fn)(struct tevent_context *ev,
+ struct tevent_req *req);
+ struct py_cli_thread *thread_state;
+
+ struct tevent_req *oplock_waiter;
+ struct py_cli_oplock_break *oplock_breaks;
+ struct py_tevent_cond *oplock_cond;
+};
+
+#ifdef HAVE_PTHREAD
+
+#include <pthread.h>
+
+struct py_cli_thread {
+
+ /*
+ * Pipe to make the poll thread wake up in our destructor, so
+ * that we can exit and join the thread.
+ */
+ int shutdown_pipe[2];
+ struct tevent_fd *shutdown_fde;
+ bool do_shutdown;
+ pthread_t id;
+
+ /*
+ * Thread state to release the GIL during the poll(2) syscall
+ */
+ PyThreadState *py_threadstate;
+};
+
+static void *py_cli_state_poll_thread(void *private_data)
+{
+ struct py_cli_state *self = (struct py_cli_state *)private_data;
+ struct py_cli_thread *t = self->thread_state;
+ PyGILState_STATE gstate;
+
+ gstate = PyGILState_Ensure();
+
+ while (!t->do_shutdown) {
+ int ret;
+ ret = tevent_loop_once(self->ev);
+ assert(ret == 0);
+ }
+ PyGILState_Release(gstate);
+ return NULL;
+}
+
+static void py_cli_state_trace_callback(enum tevent_trace_point point,
+ void *private_data)
+{
+ struct py_cli_state *self = (struct py_cli_state *)private_data;
+ struct py_cli_thread *t = self->thread_state;
+
+ switch(point) {
+ case TEVENT_TRACE_BEFORE_WAIT:
+ assert(t->py_threadstate == NULL);
+ t->py_threadstate = PyEval_SaveThread();
+ break;
+ case TEVENT_TRACE_AFTER_WAIT:
+ assert(t->py_threadstate != NULL);
+ PyEval_RestoreThread(t->py_threadstate);
+ t->py_threadstate = NULL;
+ break;
+ default:
+ break;
+ }
+}
+
+static void py_cli_state_shutdown_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct py_cli_state *self = (struct py_cli_state *)private_data;
+ struct py_cli_thread *t = self->thread_state;
+
+ if ((flags & TEVENT_FD_READ) == 0) {
+ return;
+ }
+ TALLOC_FREE(t->shutdown_fde);
+ t->do_shutdown = true;
+}
+
+static int py_cli_thread_destructor(struct py_cli_thread *t)
+{
+ char c = 0;
+ ssize_t written;
+ int ret;
+
+ do {
+ /*
+ * This will wake the poll thread from the poll(2)
+ */
+ written = write(t->shutdown_pipe[1], &c, 1);
+ } while ((written == -1) && (errno == EINTR));
+
+ /*
+ * Allow the poll thread to do its own cleanup under the GIL
+ */
+ Py_BEGIN_ALLOW_THREADS
+ ret = pthread_join(t->id, NULL);
+ Py_END_ALLOW_THREADS
+ assert(ret == 0);
+
+ if (t->shutdown_pipe[0] != -1) {
+ close(t->shutdown_pipe[0]);
+ t->shutdown_pipe[0] = -1;
+ }
+ if (t->shutdown_pipe[1] != -1) {
+ close(t->shutdown_pipe[1]);
+ t->shutdown_pipe[1] = -1;
+ }
+ return 0;
+}
+
+static int py_tevent_cond_req_wait(struct tevent_context *ev,
+ struct tevent_req *req);
+
+static bool py_cli_state_setup_mt_ev(struct py_cli_state *self)
+{
+ struct py_cli_thread *t = NULL;
+ int ret;
+
+ self->ev = tevent_context_init_byname(NULL, "poll_mt");
+ if (self->ev == NULL) {
+ goto fail;
+ }
+ samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt");
+ tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self);
+
+ self->req_wait_fn = py_tevent_cond_req_wait;
+
+ self->thread_state = talloc_zero(NULL, struct py_cli_thread);
+ if (self->thread_state == NULL) {
+ goto fail;
+ }
+ t = self->thread_state;
+
+ ret = pipe(t->shutdown_pipe);
+ if (ret == -1) {
+ goto fail;
+ }
+ t->shutdown_fde = tevent_add_fd(
+ self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ,
+ py_cli_state_shutdown_handler, self);
+ if (t->shutdown_fde == NULL) {
+ goto fail;
+ }
+
+ PyEval_InitThreads();
+
+ ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self);
+ if (ret != 0) {
+ goto fail;
+ }
+ talloc_set_destructor(self->thread_state, py_cli_thread_destructor);
+ return true;
+
+fail:
+ if (t != NULL) {
+ TALLOC_FREE(t->shutdown_fde);
+
+ if (t->shutdown_pipe[0] != -1) {
+ close(t->shutdown_pipe[0]);
+ t->shutdown_pipe[0] = -1;
+ }
+ if (t->shutdown_pipe[1] != -1) {
+ close(t->shutdown_pipe[1]);
+ t->shutdown_pipe[1] = -1;
+ }
+ }
+
+ TALLOC_FREE(self->thread_state);
+ TALLOC_FREE(self->ev);
+ return false;
+}
+
+struct py_tevent_cond {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ bool is_done;
+};
+
+static void py_tevent_signalme(struct tevent_req *req);
+
+static int py_tevent_cond_wait(struct py_tevent_cond *cond)
+{
+ int ret, result;
+
+ result = pthread_mutex_init(&cond->mutex, NULL);
+ if (result != 0) {
+ goto fail;
+ }
+ result = pthread_cond_init(&cond->cond, NULL);
+ if (result != 0) {
+ goto fail_mutex;
+ }
+
+ result = pthread_mutex_lock(&cond->mutex);
+ if (result != 0) {
+ goto fail_cond;
+ }
+
+ cond->is_done = false;
+
+ while (!cond->is_done) {
+
+ Py_BEGIN_ALLOW_THREADS
+ result = pthread_cond_wait(&cond->cond, &cond->mutex);
+ Py_END_ALLOW_THREADS
+
+ if (result != 0) {
+ goto fail_unlock;
+ }
+ }
+
+fail_unlock:
+ ret = pthread_mutex_unlock(&cond->mutex);
+ assert(ret == 0);
+fail_cond:
+ ret = pthread_cond_destroy(&cond->cond);
+ assert(ret == 0);
+fail_mutex:
+ ret = pthread_mutex_destroy(&cond->mutex);
+ assert(ret == 0);
+fail:
+ return result;
+}
+
+static int py_tevent_cond_req_wait(struct tevent_context *ev,
+ struct tevent_req *req)
+{
+ struct py_tevent_cond cond;
+ tevent_req_set_callback(req, py_tevent_signalme, &cond);
+ return py_tevent_cond_wait(&cond);
+}
+
+static void py_tevent_cond_signal(struct py_tevent_cond *cond)
+{
+ int ret;
+
+ ret = pthread_mutex_lock(&cond->mutex);
+ assert(ret == 0);
+
+ cond->is_done = true;
+
+ ret = pthread_cond_signal(&cond->cond);
+ assert(ret == 0);
+ ret = pthread_mutex_unlock(&cond->mutex);
+ assert(ret == 0);
+}
+
+static void py_tevent_signalme(struct tevent_req *req)
+{
+ struct py_tevent_cond *cond = (struct py_tevent_cond *)
+ tevent_req_callback_data_void(req);
+
+ py_tevent_cond_signal(cond);
+}
+
+#endif
+
+static int py_tevent_req_wait(struct tevent_context *ev,
+ struct tevent_req *req);
+
+static bool py_cli_state_setup_ev(struct py_cli_state *self)
+{
+ self->ev = tevent_context_init(NULL);
+ if (self->ev == NULL) {
+ return false;
+ }
+
+ samba_tevent_set_debug(self->ev, "pylibsmb_tevent");
+
+ self->req_wait_fn = py_tevent_req_wait;
+
+ return true;
+}
+
+static int py_tevent_req_wait(struct tevent_context *ev,
+ struct tevent_req *req)
+{
+ while (tevent_req_is_in_progress(req)) {
+ int ret;
+
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static bool py_tevent_req_wait_exc(struct py_cli_state *self,
+ struct tevent_req *req)
+{
+ int ret;
+
+ if (req == NULL) {
+ PyErr_NoMemory();
+ return false;
+ }
+ ret = self->req_wait_fn(self->ev, req);
+ if (ret != 0) {
+ TALLOC_FREE(req);
+ errno = ret;
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ return false;
+ }
+ return true;
+}
+
+static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args,
+ PyObject *kwds)
+{
+ struct py_cli_state *self;
+
+ self = (struct py_cli_state *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->cli = NULL;
+ self->ev = NULL;
+ self->thread_state = NULL;
+ self->oplock_waiter = NULL;
+ self->oplock_cond = NULL;
+ self->oplock_breaks = NULL;
+ return (PyObject *)self;
+}
+
+static struct smb2_negotiate_contexts *py_cli_get_negotiate_contexts(
+ TALLOC_CTX *mem_ctx, PyObject *list)
+{
+ struct smb2_negotiate_contexts *ctxs = NULL;
+ Py_ssize_t i, len;
+ int ret;
+
+ ret = PyList_Check(list);
+ if (!ret) {
+ goto fail;
+ }
+
+ len = PyList_Size(list);
+ if (len == 0) {
+ goto fail;
+ }
+
+ ctxs = talloc_zero(mem_ctx, struct smb2_negotiate_contexts);
+ if (ctxs == NULL) {
+ goto fail;
+ }
+
+ for (i=0; i<len; i++) {
+ NTSTATUS status;
+
+ PyObject *t = PyList_GetItem(list, i);
+ Py_ssize_t tlen;
+
+ PyObject *ptype = NULL;
+ long type;
+
+ PyObject *pdata = NULL;
+ DATA_BLOB data = { .data = NULL, };
+
+ if (t == NULL) {
+ goto fail;
+ }
+
+ ret = PyTuple_Check(t);
+ if (!ret) {
+ goto fail;
+ }
+
+ tlen = PyTuple_Size(t);
+ if (tlen != 2) {
+ goto fail;
+ }
+
+ ptype = PyTuple_GetItem(t, 0);
+ if (ptype == NULL) {
+ goto fail;
+ }
+ type = PyLong_AsLong(ptype);
+ if ((type < 0) || (type > UINT16_MAX)) {
+ goto fail;
+ }
+
+ pdata = PyTuple_GetItem(t, 1);
+
+ ret = PyBytes_Check(pdata);
+ if (!ret) {
+ goto fail;
+ }
+
+ data.data = (uint8_t *)PyBytes_AsString(pdata);
+ data.length = PyBytes_Size(pdata);
+
+ status = smb2_negotiate_context_add(
+ ctxs, ctxs, type, data.data, data.length);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+ return ctxs;
+
+fail:
+ TALLOC_FREE(ctxs);
+ return NULL;
+}
+
+static void py_cli_got_oplock_break(struct tevent_req *req);
+
+static int py_cli_state_init(struct py_cli_state *self, PyObject *args,
+ PyObject *kwds)
+{
+ NTSTATUS status;
+ char *host, *share;
+ PyObject *creds = NULL;
+ struct cli_credentials *cli_creds;
+ PyObject *py_lp = Py_None;
+ PyObject *py_multi_threaded = Py_False;
+ bool multi_threaded = false;
+ PyObject *py_force_smb1 = Py_False;
+ bool force_smb1 = false;
+ PyObject *py_ipc = Py_False;
+ PyObject *py_posix = Py_False;
+ PyObject *py_negotiate_contexts = NULL;
+ struct smb2_negotiate_contexts *negotiate_contexts = NULL;
+ bool use_ipc = false;
+ bool request_posix = false;
+ struct tevent_req *req;
+ bool ret;
+ int flags = 0;
+
+ static const char *kwlist[] = {
+ "host", "share", "lp", "creds",
+ "multi_threaded", "force_smb1",
+ "ipc",
+ "posix",
+ "negotiate_contexts",
+ NULL
+ };
+
+ PyTypeObject *py_type_Credentials = get_pytype(
+ "samba.credentials", "Credentials");
+ if (py_type_Credentials == NULL) {
+ return -1;
+ }
+
+ ret = ParseTupleAndKeywords(
+ args, kwds, "ssO|O!OOOOO", kwlist,
+ &host, &share, &py_lp,
+ py_type_Credentials, &creds,
+ &py_multi_threaded,
+ &py_force_smb1,
+ &py_ipc,
+ &py_posix,
+ &py_negotiate_contexts);
+
+ Py_DECREF(py_type_Credentials);
+
+ if (!ret) {
+ return -1;
+ }
+
+ multi_threaded = PyObject_IsTrue(py_multi_threaded);
+ force_smb1 = PyObject_IsTrue(py_force_smb1);
+
+ if (force_smb1) {
+ /*
+ * As most of the cli_*_send() function
+ * don't support SMB2 (it's only plugged
+ * into the sync wrapper functions currently)
+ * we have a way to force SMB1.
+ */
+ flags = CLI_FULL_CONNECTION_FORCE_SMB1;
+ }
+
+ use_ipc = PyObject_IsTrue(py_ipc);
+ if (use_ipc) {
+ flags |= CLI_FULL_CONNECTION_IPC;
+ }
+
+ request_posix = PyObject_IsTrue(py_posix);
+ if (request_posix) {
+ flags |= CLI_FULL_CONNECTION_REQUEST_POSIX;
+ }
+
+ if (py_negotiate_contexts != NULL) {
+ negotiate_contexts = py_cli_get_negotiate_contexts(
+ talloc_tos(), py_negotiate_contexts);
+ if (negotiate_contexts == NULL) {
+ return -1;
+ }
+ }
+
+ if (multi_threaded) {
+#ifdef HAVE_PTHREAD
+ ret = py_cli_state_setup_mt_ev(self);
+ if (!ret) {
+ return -1;
+ }
+#else
+ PyErr_SetString(PyExc_RuntimeError,
+ "No PTHREAD support available");
+ return -1;
+#endif
+ } else {
+ ret = py_cli_state_setup_ev(self);
+ if (!ret) {
+ return -1;
+ }
+ }
+
+ if (creds == NULL) {
+ cli_creds = cli_credentials_init_anon(NULL);
+ } else {
+ cli_creds = PyCredentials_AsCliCredentials(creds);
+ }
+
+ req = cli_full_connection_creds_send(
+ NULL, self->ev, "myname", host, NULL, 0, share, "?????",
+ cli_creds, flags,
+ negotiate_contexts);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return -1;
+ }
+ status = cli_full_connection_creds_recv(req, &self->cli);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return -1;
+ }
+
+ /*
+ * Oplocks require a multi threaded connection
+ */
+ if (self->thread_state == NULL) {
+ return 0;
+ }
+
+ self->oplock_waiter = cli_smb_oplock_break_waiter_send(
+ self->ev, self->ev, self->cli);
+ if (self->oplock_waiter == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
+ self);
+ return 0;
+}
+
+static void py_cli_got_oplock_break(struct tevent_req *req)
+{
+ struct py_cli_state *self = (struct py_cli_state *)
+ tevent_req_callback_data_void(req);
+ struct py_cli_oplock_break b;
+ struct py_cli_oplock_break *tmp;
+ size_t num_breaks;
+ NTSTATUS status;
+
+ status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level);
+ TALLOC_FREE(req);
+ self->oplock_waiter = NULL;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ num_breaks = talloc_array_length(self->oplock_breaks);
+ tmp = talloc_realloc(self->ev, self->oplock_breaks,
+ struct py_cli_oplock_break, num_breaks+1);
+ if (tmp == NULL) {
+ return;
+ }
+ self->oplock_breaks = tmp;
+ self->oplock_breaks[num_breaks] = b;
+
+ if (self->oplock_cond != NULL) {
+ py_tevent_cond_signal(self->oplock_cond);
+ }
+
+ self->oplock_waiter = cli_smb_oplock_break_waiter_send(
+ self->ev, self->ev, self->cli);
+ if (self->oplock_waiter == NULL) {
+ return;
+ }
+ tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break,
+ self);
+}
+
+static PyObject *py_cli_get_oplock_break(struct py_cli_state *self,
+ PyObject *args)
+{
+ size_t num_oplock_breaks;
+
+ if (!PyArg_ParseTuple(args, "")) {
+ return NULL;
+ }
+
+ if (self->thread_state == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "get_oplock_break() only possible on "
+ "a multi_threaded connection");
+ return NULL;
+ }
+
+ if (self->oplock_cond != NULL) {
+ errno = EBUSY;
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ return NULL;
+ }
+
+ num_oplock_breaks = talloc_array_length(self->oplock_breaks);
+
+ if (num_oplock_breaks == 0) {
+ struct py_tevent_cond cond;
+ int ret;
+
+ self->oplock_cond = &cond;
+ ret = py_tevent_cond_wait(&cond);
+ self->oplock_cond = NULL;
+
+ if (ret != 0) {
+ errno = ret;
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ return NULL;
+ }
+ }
+
+ num_oplock_breaks = talloc_array_length(self->oplock_breaks);
+ if (num_oplock_breaks > 0) {
+ PyObject *result;
+
+ result = Py_BuildValue(
+ "{s:i,s:i}",
+ "fnum", self->oplock_breaks[0].fnum,
+ "level", self->oplock_breaks[0].level);
+
+ memmove(&self->oplock_breaks[0], &self->oplock_breaks[1],
+ sizeof(self->oplock_breaks[0]) *
+ (num_oplock_breaks - 1));
+ self->oplock_breaks = talloc_realloc(
+ NULL, self->oplock_breaks, struct py_cli_oplock_break,
+ num_oplock_breaks - 1);
+
+ return result;
+ }
+ Py_RETURN_NONE;
+}
+
+static void py_cli_state_dealloc(struct py_cli_state *self)
+{
+ TALLOC_FREE(self->thread_state);
+ TALLOC_FREE(self->oplock_waiter);
+ TALLOC_FREE(self->ev);
+
+ if (self->cli != NULL) {
+ cli_shutdown(self->cli);
+ self->cli = NULL;
+ }
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args)
+{
+ unsigned int nmsecs = 0;
+ unsigned int omsecs = 0;
+
+ if (!PyArg_ParseTuple(args, "I", &nmsecs)) {
+ return NULL;
+ }
+
+ omsecs = cli_set_timeout(self->cli, nmsecs);
+
+ return PyLong_FromLong(omsecs);
+}
+
+static PyObject *py_cli_echo(struct py_cli_state *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ DATA_BLOB data = data_blob_string_const("keepalive");
+ struct tevent_req *req = NULL;
+ NTSTATUS status;
+
+ req = cli_echo_send(NULL, self->ev, self->cli, 1, data);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_echo_recv(req);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args,
+ PyObject *kwds)
+{
+ char *fname;
+ unsigned CreateFlags = 0;
+ unsigned DesiredAccess = FILE_GENERIC_READ;
+ unsigned FileAttributes = 0;
+ unsigned ShareAccess = 0;
+ unsigned CreateDisposition = FILE_OPEN;
+ unsigned CreateOptions = 0;
+ unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
+ unsigned SecurityFlags = 0;
+ uint16_t fnum;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ static const char *kwlist[] = {
+ "Name", "CreateFlags", "DesiredAccess", "FileAttributes",
+ "ShareAccess", "CreateDisposition", "CreateOptions",
+ "ImpersonationLevel", "SecurityFlags", NULL };
+
+ if (!ParseTupleAndKeywords(
+ args, kwds, "s|IIIIIIII", kwlist,
+ &fname, &CreateFlags, &DesiredAccess, &FileAttributes,
+ &ShareAccess, &CreateDisposition, &CreateOptions,
+ &ImpersonationLevel, &SecurityFlags)) {
+ return NULL;
+ }
+
+ req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags,
+ DesiredAccess, FileAttributes, ShareAccess,
+ CreateDisposition, CreateOptions,
+ ImpersonationLevel, SecurityFlags);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_ntcreate_recv(req, &fnum, NULL);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ return Py_BuildValue("I", (unsigned)fnum);
+}
+
+static struct smb2_create_blobs *py_cli_get_create_contexts(
+ TALLOC_CTX *mem_ctx, PyObject *list)
+{
+ struct smb2_create_blobs *ctxs = NULL;
+ Py_ssize_t i, len;
+ int ret;
+
+ ret = PyList_Check(list);
+ if (!ret) {
+ goto fail;
+ }
+
+ len = PyList_Size(list);
+ if (len == 0) {
+ goto fail;
+ }
+
+ ctxs = talloc_zero(mem_ctx, struct smb2_create_blobs);
+ if (ctxs == NULL) {
+ goto fail;
+ }
+
+ for (i=0; i<len; i++) {
+ NTSTATUS status;
+
+ PyObject *t = NULL;
+ Py_ssize_t tlen;
+
+ PyObject *pname = NULL;
+ char *name = NULL;
+
+ PyObject *pdata = NULL;
+ DATA_BLOB data = { .data = NULL, };
+
+ t = PyList_GetItem(list, i);
+ if (t == NULL) {
+ goto fail;
+ }
+
+ ret = PyTuple_Check(t);
+ if (!ret) {
+ goto fail;
+ }
+
+ tlen = PyTuple_Size(t);
+ if (tlen != 2) {
+ goto fail;
+ }
+
+ pname = PyTuple_GetItem(t, 0);
+ if (pname == NULL) {
+ goto fail;
+ }
+ ret = PyBytes_Check(pname);
+ if (!ret) {
+ goto fail;
+ }
+ name = PyBytes_AsString(pname);
+
+ pdata = PyTuple_GetItem(t, 1);
+ if (pdata == NULL) {
+ goto fail;
+ }
+ ret = PyBytes_Check(pdata);
+ if (!ret) {
+ goto fail;
+ }
+ data = (DATA_BLOB) {
+ .data = (uint8_t *)PyBytes_AsString(pdata),
+ .length = PyBytes_Size(pdata),
+ };
+ status = smb2_create_blob_add(ctxs, ctxs, name, data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+ return ctxs;
+
+fail:
+ TALLOC_FREE(ctxs);
+ return NULL;
+}
+
+static PyObject *py_cli_create_contexts(const struct smb2_create_blobs *blobs)
+{
+ PyObject *py_blobs = NULL;
+ uint32_t i;
+
+ if (blobs == NULL) {
+ Py_RETURN_NONE;
+ }
+
+ py_blobs = PyList_New(blobs->num_blobs);
+ if (py_blobs == NULL) {
+ return NULL;
+ }
+
+ for (i=0; i<blobs->num_blobs; i++) {
+ struct smb2_create_blob *blob = &blobs->blobs[i];
+ PyObject *py_blob = NULL;
+ int ret;
+
+ py_blob = Py_BuildValue(
+ "(yy#)",
+ blob->tag,
+ blob->data.data,
+ (int)blob->data.length);
+ if (py_blob == NULL) {
+ goto fail;
+ }
+
+ ret = PyList_SetItem(py_blobs, i, py_blob);
+ if (ret == -1) {
+ Py_XDECREF(py_blob);
+ goto fail;
+ }
+ }
+ return py_blobs;
+
+fail:
+ Py_XDECREF(py_blobs);
+ return NULL;
+}
+
+static PyObject *py_cli_create_returns(const struct smb_create_returns *r)
+{
+ PyObject *v = NULL;
+
+ v = Py_BuildValue(
+ "{sLsLsLsLsLsLsLsLsLsL}",
+ "oplock_level",
+ (unsigned long long)r->oplock_level,
+ "flags",
+ (unsigned long long)r->flags,
+ "create_action",
+ (unsigned long long)r->create_action,
+ "creation_time",
+ (unsigned long long)r->creation_time,
+ "last_access_time",
+ (unsigned long long)r->last_access_time,
+ "last_write_time",
+ (unsigned long long)r->last_write_time,
+ "change_time",
+ (unsigned long long)r->change_time,
+ "allocation_size",
+ (unsigned long long)r->allocation_size,
+ "end_of_file",
+ (unsigned long long)r->end_of_file,
+ "file_attributes",
+ (unsigned long long)r->file_attributes);
+ return v;
+}
+
+static PyObject *py_cli_symlink_error(const struct symlink_reparse_struct *s)
+{
+ char *subst_utf8 = NULL, *print_utf8 = NULL;
+ size_t subst_utf8_len, print_utf8_len;
+ PyObject *v = NULL;
+ bool ok = true;
+
+ /*
+ * Python wants utf-8, regardless of our unix charset (which
+ * most likely is utf-8 these days, but you never know).
+ */
+
+ ok = convert_string_talloc(
+ talloc_tos(),
+ CH_UNIX,
+ CH_UTF8,
+ s->substitute_name,
+ strlen(s->substitute_name),
+ &subst_utf8,
+ &subst_utf8_len);
+ if (!ok) {
+ goto fail;
+ }
+
+ ok = convert_string_talloc(
+ talloc_tos(),
+ CH_UNIX,
+ CH_UTF8,
+ s->print_name,
+ strlen(s->print_name),
+ &print_utf8,
+ &print_utf8_len);
+ if (!ok) {
+ goto fail;
+ }
+
+ v = Py_BuildValue(
+ "{sLsssssL}",
+ "unparsed_path_length",
+ (unsigned long long)s->unparsed_path_length,
+ "substitute_name",
+ subst_utf8,
+ "print_name",
+ print_utf8,
+ "flags",
+ (unsigned long long)s->flags);
+
+fail:
+ TALLOC_FREE(subst_utf8);
+ TALLOC_FREE(print_utf8);
+ return v;
+}
+
+static PyObject *py_cli_create_ex(
+ struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+ char *fname = NULL;
+ unsigned CreateFlags = 0;
+ unsigned DesiredAccess = FILE_GENERIC_READ;
+ unsigned FileAttributes = 0;
+ unsigned ShareAccess = 0;
+ unsigned CreateDisposition = FILE_OPEN;
+ unsigned CreateOptions = 0;
+ unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
+ unsigned SecurityFlags = 0;
+ PyObject *py_create_contexts_in = NULL;
+ PyObject *py_create_contexts_out = NULL;
+ struct smb2_create_blobs *create_contexts_in = NULL;
+ struct smb2_create_blobs create_contexts_out = { .num_blobs = 0 };
+ struct smb_create_returns cr = { .create_action = 0, };
+ struct symlink_reparse_struct *symlink = NULL;
+ PyObject *py_cr = NULL;
+ uint16_t fnum;
+ struct tevent_req *req;
+ NTSTATUS status;
+ int ret;
+ bool ok;
+ PyObject *v = NULL;
+
+ static const char *kwlist[] = {
+ "Name",
+ "CreateFlags",
+ "DesiredAccess",
+ "FileAttributes",
+ "ShareAccess",
+ "CreateDisposition",
+ "CreateOptions",
+ "ImpersonationLevel",
+ "SecurityFlags",
+ "CreateContexts",
+ NULL };
+
+ ret = ParseTupleAndKeywords(
+ args,
+ kwds,
+ "s|IIIIIIIIO",
+ kwlist,
+ &fname,
+ &CreateFlags,
+ &DesiredAccess,
+ &FileAttributes,
+ &ShareAccess,
+ &CreateDisposition,
+ &CreateOptions,
+ &ImpersonationLevel,
+ &SecurityFlags,
+ &py_create_contexts_in);
+ if (!ret) {
+ return NULL;
+ }
+
+ if (py_create_contexts_in != NULL) {
+ create_contexts_in = py_cli_get_create_contexts(
+ NULL, py_create_contexts_in);
+ if (create_contexts_in == NULL) {
+ errno = EINVAL;
+ PyErr_SetFromErrno(PyExc_RuntimeError);
+ return NULL;
+ }
+ }
+
+ if (smbXcli_conn_protocol(self->cli->conn) >= PROTOCOL_SMB2_02) {
+ struct cli_smb2_create_flags cflags = {
+ .batch_oplock = (CreateFlags & REQUEST_BATCH_OPLOCK),
+ .exclusive_oplock = (CreateFlags & REQUEST_OPLOCK),
+ };
+
+ req = cli_smb2_create_fnum_send(
+ NULL,
+ self->ev,
+ self->cli,
+ fname,
+ cflags,
+ ImpersonationLevel,
+ DesiredAccess,
+ FileAttributes,
+ ShareAccess,
+ CreateDisposition,
+ CreateOptions,
+ create_contexts_in);
+ } else {
+ req = cli_ntcreate_send(
+ NULL,
+ self->ev,
+ self->cli,
+ fname,
+ CreateFlags,
+ DesiredAccess,
+ FileAttributes,
+ ShareAccess,
+ CreateDisposition,
+ CreateOptions,
+ ImpersonationLevel,
+ SecurityFlags);
+ }
+
+ TALLOC_FREE(create_contexts_in);
+
+ ok = py_tevent_req_wait_exc(self, req);
+ if (!ok) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(self->cli->conn) >= PROTOCOL_SMB2_02) {
+ status = cli_smb2_create_fnum_recv(
+ req,
+ &fnum,
+ &cr,
+ NULL,
+ &create_contexts_out,
+ &symlink);
+ } else {
+ status = cli_ntcreate_recv(req, &fnum, &cr);
+ }
+
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ SMB_ASSERT(symlink == NULL);
+
+ py_create_contexts_out = py_cli_create_contexts(&create_contexts_out);
+ TALLOC_FREE(create_contexts_out.blobs);
+ if (py_create_contexts_out == NULL) {
+ goto nomem;
+ }
+
+ py_cr = py_cli_create_returns(&cr);
+ if (py_cr == NULL) {
+ goto nomem;
+ }
+
+ v = Py_BuildValue("(IOO)",
+ (unsigned)fnum,
+ py_cr,
+ py_create_contexts_out);
+ return v;
+nomem:
+ status = NT_STATUS_NO_MEMORY;
+fail:
+ Py_XDECREF(py_create_contexts_out);
+ Py_XDECREF(py_cr);
+ Py_XDECREF(v);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
+ (symlink != NULL)) {
+ PyErr_SetObject(
+ PyObject_GetAttrString(
+ PyImport_ImportModule("samba"),
+ "NTSTATUSError"),
+ Py_BuildValue(
+ "I,s,O",
+ NT_STATUS_V(status),
+ get_friendly_nt_error_msg(status),
+ py_cli_symlink_error(symlink)));
+ } else {
+ PyErr_SetNTSTATUS(status);
+ }
+ return NULL;
+}
+
+static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args)
+{
+ struct tevent_req *req;
+ int fnum;
+ int flags = 0;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "i|i", &fnum, &flags)) {
+ return NULL;
+ }
+
+ req = cli_close_send(NULL, self->ev, self->cli, fnum, flags);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_close_recv(req);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_rename(
+ struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+ char *fname_src = NULL, *fname_dst = NULL;
+ int replace = false;
+ struct tevent_req *req = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ static const char *kwlist[] = { "src", "dst", "replace", NULL };
+
+ ok = ParseTupleAndKeywords(
+ args, kwds, "ss|p", kwlist, &fname_src, &fname_dst, &replace);
+ if (!ok) {
+ return NULL;
+ }
+
+ req = cli_rename_send(
+ NULL, self->ev, self->cli, fname_src, fname_dst, replace);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_rename_recv(req);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+
+struct push_state {
+ char *data;
+ off_t nread;
+ off_t total_data;
+};
+
+/*
+ * cli_push() helper to write a chunk of data to a remote file
+ */
+static size_t push_data(uint8_t *buf, size_t n, void *priv)
+{
+ struct push_state *state = (struct push_state *)priv;
+ char *curr_ptr = NULL;
+ off_t remaining;
+ size_t copied_bytes;
+
+ if (state->nread >= state->total_data) {
+ return 0;
+ }
+
+ curr_ptr = state->data + state->nread;
+ remaining = state->total_data - state->nread;
+ copied_bytes = MIN(remaining, n);
+
+ memcpy(buf, curr_ptr, copied_bytes);
+ state->nread += copied_bytes;
+ return copied_bytes;
+}
+
+/*
+ * Writes a file with the contents specified
+ */
+static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args)
+{
+ uint16_t fnum;
+ const char *filename = NULL;
+ char *data = NULL;
+ Py_ssize_t size = 0;
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+ struct push_state state;
+
+ if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename,
+ &data, &size)) {
+ return NULL;
+ }
+
+ /* create a new file handle for writing to */
+ req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
+ FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_ntcreate_recv(req, &fnum, NULL);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ /* write the new file contents */
+ state.data = data;
+ state.nread = 0;
+ state.total_data = size;
+
+ req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0,
+ push_data, &state);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_push_recv(req);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ /* close the file handle */
+ req = cli_close_send(NULL, self->ev, self->cli, fnum, 0);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_close_recv(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args,
+ PyObject *kwds)
+{
+ int fnum;
+ unsigned mode = 0;
+ char *buf;
+ Py_ssize_t buflen;
+ unsigned long long offset;
+ struct tevent_req *req;
+ NTSTATUS status;
+ size_t written;
+
+ static const char *kwlist[] = {
+ "fnum", "buffer", "offset", "mode", NULL };
+
+ if (!ParseTupleAndKeywords(
+ args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist,
+ &fnum, &buf, &buflen, &offset, &mode)) {
+ return NULL;
+ }
+
+ req = cli_write_send(NULL, self->ev, self->cli, fnum, mode,
+ (uint8_t *)buf, offset, buflen);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_write_recv(req, &written);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ return Py_BuildValue("K", (unsigned long long)written);
+}
+
+/*
+ * Returns the size of the given file
+ */
+static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum,
+ off_t *size)
+{
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+
+ req = cli_qfileinfo_basic_send(NULL, self->ev, self->cli, fnum);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ status = cli_qfileinfo_basic_recv(
+ req, NULL, size, NULL, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(req);
+ return status;
+}
+
+/*
+ * Loads the specified file's contents and returns it
+ */
+static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *filename = NULL;
+ struct tevent_req *req = NULL;
+ uint16_t fnum;
+ off_t size;
+ char *buf = NULL;
+ off_t nread = 0;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) {
+ return NULL;
+ }
+
+ /* get a read file handle */
+ req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0,
+ FILE_READ_DATA | FILE_READ_ATTRIBUTES,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ, FILE_OPEN, 0,
+ SMB2_IMPERSONATION_IMPERSONATION, 0);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_ntcreate_recv(req, &fnum, NULL);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ /* get a buffer to hold the file contents */
+ status = py_smb_filesize(self, fnum, &size);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ result = PyBytes_FromStringAndSize(NULL, size);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ /* read the file contents */
+ buf = PyBytes_AS_STRING(result);
+ req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size,
+ size, cli_read_sink, &buf);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ Py_XDECREF(result);
+ return NULL;
+ }
+ status = cli_pull_recv(req, &nread);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ Py_XDECREF(result);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ /* close the file handle */
+ req = cli_close_send(NULL, self->ev, self->cli, fnum, 0);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ Py_XDECREF(result);
+ return NULL;
+ }
+ status = cli_close_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ Py_XDECREF(result);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ /* sanity-check we read the expected number of bytes */
+ if (nread > size) {
+ Py_XDECREF(result);
+ PyErr_Format(PyExc_IOError,
+ "read invalid - got %zu requested %zu",
+ nread, size);
+ return NULL;
+ }
+
+ if (nread < size) {
+ if (_PyBytes_Resize(&result, nread) < 0) {
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args,
+ PyObject *kwds)
+{
+ int fnum;
+ unsigned long long offset;
+ unsigned size;
+ struct tevent_req *req;
+ NTSTATUS status;
+ char *buf;
+ size_t received;
+ PyObject *result;
+
+ static const char *kwlist[] = {
+ "fnum", "offset", "size", NULL };
+
+ if (!ParseTupleAndKeywords(
+ args, kwds, "iKI", kwlist, &fnum, &offset,
+ &size)) {
+ return NULL;
+ }
+
+ result = PyBytes_FromStringAndSize(NULL, size);
+ if (result == NULL) {
+ return NULL;
+ }
+ buf = PyBytes_AS_STRING(result);
+
+ req = cli_read_send(NULL, self->ev, self->cli, fnum,
+ buf, offset, size);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ Py_XDECREF(result);
+ return NULL;
+ }
+ status = cli_read_recv(req, &received);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ Py_XDECREF(result);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ if (received > size) {
+ Py_XDECREF(result);
+ PyErr_Format(PyExc_IOError,
+ "read invalid - got %zu requested %u",
+ received, size);
+ return NULL;
+ }
+
+ if (received < size) {
+ if (_PyBytes_Resize(&result, received) < 0) {
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args,
+ PyObject *kwds)
+{
+ int fnum;
+ unsigned long long size;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ static const char *kwlist[] = {
+ "fnum", "size", NULL };
+
+ if (!ParseTupleAndKeywords(
+ args, kwds, "IK", kwlist, &fnum, &size)) {
+ return NULL;
+ }
+
+ req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_ftruncate_recv(req);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_delete_on_close(struct py_cli_state *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ unsigned fnum, flag;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ static const char *kwlist[] = {
+ "fnum", "flag", NULL };
+
+ if (!ParseTupleAndKeywords(
+ args, kwds, "II", kwlist, &fnum, &flag)) {
+ return NULL;
+ }
+
+ req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum,
+ flag);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_nt_delete_on_close_recv(req);
+ TALLOC_FREE(req);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+struct py_cli_notify_state {
+ PyObject_HEAD
+ struct py_cli_state *py_cli_state;
+ struct tevent_req *req;
+};
+
+static void py_cli_notify_state_dealloc(struct py_cli_notify_state *self)
+{
+ TALLOC_FREE(self->req);
+ Py_CLEAR(self->py_cli_state);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyTypeObject py_cli_notify_state_type;
+
+static PyObject *py_cli_notify(struct py_cli_state *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ static const char *kwlist[] = {
+ "fnum",
+ "buffer_size",
+ "completion_filter",
+ "recursive",
+ NULL
+ };
+ unsigned fnum = 0;
+ unsigned buffer_size = 0;
+ unsigned completion_filter = 0;
+ PyObject *py_recursive = Py_False;
+ bool recursive = false;
+ struct tevent_req *req = NULL;
+ struct tevent_queue *send_queue = NULL;
+ struct tevent_req *flush_req = NULL;
+ bool ok;
+ struct py_cli_notify_state *py_notify_state = NULL;
+ struct timeval endtime;
+
+ ok = ParseTupleAndKeywords(args,
+ kwds,
+ "IIIO",
+ kwlist,
+ &fnum,
+ &buffer_size,
+ &completion_filter,
+ &py_recursive);
+ if (!ok) {
+ return NULL;
+ }
+
+ recursive = PyObject_IsTrue(py_recursive);
+
+ req = cli_notify_send(NULL,
+ self->ev,
+ self->cli,
+ fnum,
+ buffer_size,
+ completion_filter,
+ recursive);
+ if (req == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ /*
+ * Just wait for the request being submitted to
+ * the kernel/socket/wire.
+ */
+ send_queue = smbXcli_conn_send_queue(self->cli->conn);
+ flush_req = tevent_queue_wait_send(req,
+ self->ev,
+ send_queue);
+ endtime = timeval_current_ofs_msec(self->cli->timeout);
+ ok = tevent_req_set_endtime(flush_req,
+ self->ev,
+ endtime);
+ if (!ok) {
+ TALLOC_FREE(req);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ ok = py_tevent_req_wait_exc(self, flush_req);
+ if (!ok) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ TALLOC_FREE(flush_req);
+
+ py_notify_state = (struct py_cli_notify_state *)
+ py_cli_notify_state_type.tp_alloc(&py_cli_notify_state_type, 0);
+ if (py_notify_state == NULL) {
+ TALLOC_FREE(req);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Py_INCREF(self);
+ py_notify_state->py_cli_state = self;
+ py_notify_state->req = req;
+
+ return (PyObject *)py_notify_state;
+}
+
+static PyObject *py_cli_notify_get_changes(struct py_cli_notify_state *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ struct py_cli_state *py_cli_state = self->py_cli_state;
+ struct tevent_req *req = self->req;
+ uint32_t i;
+ uint32_t num_changes = 0;
+ struct notify_change *changes = NULL;
+ PyObject *result = NULL;
+ NTSTATUS status;
+ bool ok;
+ static const char *kwlist[] = {
+ "wait",
+ NULL
+ };
+ PyObject *py_wait = Py_False;
+ bool wait = false;
+ bool pending;
+
+ ok = ParseTupleAndKeywords(args,
+ kwds,
+ "O",
+ kwlist,
+ &py_wait);
+ if (!ok) {
+ return NULL;
+ }
+
+ wait = PyObject_IsTrue(py_wait);
+
+ if (req == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "TODO req == NULL "
+ "- missing change notify request?");
+ return NULL;
+ }
+
+ pending = tevent_req_is_in_progress(req);
+ if (pending && !wait) {
+ Py_RETURN_NONE;
+ }
+
+ if (pending) {
+ struct timeval endtime;
+
+ endtime = timeval_current_ofs_msec(py_cli_state->cli->timeout);
+ ok = tevent_req_set_endtime(req,
+ py_cli_state->ev,
+ endtime);
+ if (!ok) {
+ TALLOC_FREE(req);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ }
+
+ ok = py_tevent_req_wait_exc(py_cli_state, req);
+ self->req = NULL;
+ Py_CLEAR(self->py_cli_state);
+ if (!ok) {
+ return NULL;
+ }
+
+ status = cli_notify_recv(req, req, &num_changes, &changes);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(req);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ result = Py_BuildValue("[]");
+ if (result == NULL) {
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ for (i = 0; i < num_changes; i++) {
+ PyObject *change = NULL;
+ int ret;
+
+ change = Py_BuildValue("{s:s,s:I}",
+ "name", changes[i].name,
+ "action", changes[i].action);
+ if (change == NULL) {
+ Py_XDECREF(result);
+ TALLOC_FREE(req);
+ return NULL;
+ }
+
+ ret = PyList_Append(result, change);
+ Py_DECREF(change);
+ if (ret == -1) {
+ Py_XDECREF(result);
+ TALLOC_FREE(req);
+ return NULL;
+ }
+ }
+
+ TALLOC_FREE(req);
+ return result;
+}
+
+static PyMethodDef py_cli_notify_state_methods[] = {
+ {
+ .ml_name = "get_changes",
+ .ml_meth = (PyCFunction)py_cli_notify_get_changes,
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = "Wait for change notifications: \n"
+ "N.get_changes(wait=BOOLEAN) -> "
+ "change notifications as a dictionary\n"
+ "\t\tList contents of a directory. The keys are, \n"
+ "\t\t\tname: name of changed object\n"
+ "\t\t\taction: type of the change\n"
+ "None is returned if there's no response yet and "
+ "wait=False is passed"
+ },
+ {
+ .ml_name = NULL
+ }
+};
+
+static PyTypeObject py_cli_notify_state_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libsmb_samba_cwrapper.Notify",
+ .tp_basicsize = sizeof(struct py_cli_notify_state),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = "notify request",
+ .tp_dealloc = (destructor)py_cli_notify_state_dealloc,
+ .tp_methods = py_cli_notify_state_methods,
+};
+
+/*
+ * Helper to add posix directory listing entries to an overall Python list
+ */
+static NTSTATUS list_posix_helper(struct file_info *finfo,
+ const char *mask, void *state)
+{
+ PyObject *result = (PyObject *)state;
+ PyObject *file = NULL;
+ int ret;
+
+ /*
+ * Build a dictionary representing the file info.
+ */
+ file = Py_BuildValue("{s:s,s:I,"
+ "s:K,s:K,"
+ "s:l,s:l,s:l,s:l,"
+ "s:i,s:K,s:i,s:i,s:I,"
+ "s:s,s:s}",
+ "name", finfo->name,
+ "attrib", finfo->attr,
+
+ "size", finfo->size,
+ "allocaction_size", finfo->allocated_size,
+
+ "btime",
+ convert_timespec_to_time_t(finfo->btime_ts),
+ "atime",
+ convert_timespec_to_time_t(finfo->atime_ts),
+ "mtime",
+ convert_timespec_to_time_t(finfo->mtime_ts),
+ "ctime",
+ convert_timespec_to_time_t(finfo->ctime_ts),
+
+ "perms", finfo->st_ex_mode,
+ "ino", finfo->ino,
+ "dev", finfo->st_ex_dev,
+ "nlink", finfo->st_ex_nlink,
+ "reparse_tag", finfo->reparse_tag,
+
+ "owner_sid",
+ dom_sid_string(finfo, &finfo->owner_sid),
+ "group_sid",
+ dom_sid_string(finfo, &finfo->group_sid));
+ if (file == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = PyList_Append(result, file);
+ Py_CLEAR(file);
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Helper to add directory listing entries to an overall Python list
+ */
+static NTSTATUS list_helper(struct file_info *finfo,
+ const char *mask, void *state)
+{
+ PyObject *result = (PyObject *)state;
+ PyObject *file = NULL;
+ PyObject *size = NULL;
+ int ret;
+
+ /* suppress '.' and '..' in the results we return */
+ if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) {
+ return NT_STATUS_OK;
+ }
+ size = PyLong_FromUnsignedLongLong(finfo->size);
+ /*
+ * Build a dictionary representing the file info.
+ * Note: Windows does not always return short_name (so it may be None)
+ */
+ file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}",
+ "name", finfo->name,
+ "attrib", (int)finfo->attr,
+ "short_name", finfo->short_name,
+ "size", size,
+ "mtime",
+ convert_timespec_to_time_t(finfo->mtime_ts));
+
+ Py_CLEAR(size);
+
+ if (file == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (finfo->attr & FILE_ATTRIBUTE_REPARSE_POINT) {
+ unsigned long tag = finfo->reparse_tag;
+
+ ret = PyDict_SetItemString(
+ file,
+ "reparse_tag",
+ PyLong_FromUnsignedLong(tag));
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ ret = PyList_Append(result, file);
+ Py_CLEAR(file);
+ if (ret == -1) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct do_listing_state {
+ const char *mask;
+ NTSTATUS (*callback_fn)(
+ struct file_info *finfo,
+ const char *mask,
+ void *private_data);
+ void *private_data;
+ NTSTATUS status;
+};
+
+static void do_listing_cb(struct tevent_req *subreq)
+{
+ struct do_listing_state *state = tevent_req_callback_data_void(subreq);
+ struct file_info *finfo = NULL;
+
+ state->status = cli_list_recv(subreq, NULL, &finfo);
+ if (!NT_STATUS_IS_OK(state->status)) {
+ return;
+ }
+ state->callback_fn(finfo, state->mask, state->private_data);
+ TALLOC_FREE(finfo);
+}
+
+static NTSTATUS do_listing(struct py_cli_state *self,
+ const char *base_dir, const char *user_mask,
+ uint16_t attribute,
+ unsigned int info_level,
+ NTSTATUS (*callback_fn)(struct file_info *,
+ const char *, void *),
+ void *priv)
+{
+ char *mask = NULL;
+ struct do_listing_state state = {
+ .mask = mask,
+ .callback_fn = callback_fn,
+ .private_data = priv,
+ };
+ struct tevent_req *req = NULL;
+ NTSTATUS status;
+
+ if (user_mask == NULL) {
+ mask = talloc_asprintf(NULL, "%s\\*", base_dir);
+ } else {
+ mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask);
+ }
+
+ if (mask == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ dos_format(mask);
+
+ req = cli_list_send(NULL, self->ev, self->cli, mask, attribute,
+ info_level);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ tevent_req_set_callback(req, do_listing_cb, &state);
+
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ TALLOC_FREE(req);
+
+ status = state.status;
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) {
+ status = NT_STATUS_OK;
+ }
+
+done:
+ TALLOC_FREE(mask);
+ return status;
+}
+
+static PyObject *py_cli_list(struct py_cli_state *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ char *base_dir;
+ char *user_mask = NULL;
+ unsigned int attribute = LIST_ATTRIBUTE_MASK;
+ unsigned int info_level = 0;
+ NTSTATUS status;
+ enum protocol_types proto = smbXcli_conn_protocol(self->cli->conn);
+ PyObject *result = NULL;
+ const char *kwlist[] = { "directory", "mask", "attribs",
+ "info_level", NULL };
+ NTSTATUS (*callback_fn)(struct file_info *, const char *, void *) =
+ &list_helper;
+
+ if (!ParseTupleAndKeywords(args, kwds, "z|sII:list", kwlist,
+ &base_dir, &user_mask, &attribute,
+ &info_level)) {
+ return NULL;
+ }
+
+ result = Py_BuildValue("[]");
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (!info_level) {
+ if (proto >= PROTOCOL_SMB2_02) {
+ info_level = SMB2_FIND_ID_BOTH_DIRECTORY_INFO;
+ } else {
+ info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+ }
+ }
+
+ if (info_level == SMB2_FIND_POSIX_INFORMATION) {
+ callback_fn = &list_posix_helper;
+ }
+ status = do_listing(self, base_dir, user_mask, attribute,
+ info_level, callback_fn, result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ Py_XDECREF(result);
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *filename = NULL;
+ struct tevent_req *req = NULL;
+ const uint32_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+
+ if (!PyArg_ParseTuple(args, "s:unlink", &filename)) {
+ return NULL;
+ }
+
+ req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_unlink_recv(req);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+ const char *dirname = NULL;
+
+ if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) {
+ return NULL;
+ }
+
+ req = cli_rmdir_send(NULL, self->ev, self->cli, dirname);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_rmdir_recv(req);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+/*
+ * Create a directory
+ */
+static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *dirname = NULL;
+ struct tevent_req *req = NULL;
+
+ if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) {
+ return NULL;
+ }
+
+ req = cli_mkdir_send(NULL, self->ev, self->cli, dirname);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_mkdir_recv(req);
+ TALLOC_FREE(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+/*
+ * Does a whoami call
+ */
+static PyObject *py_smb_posix_whoami(struct py_cli_state *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+ uint64_t uid;
+ uint64_t gid;
+ uint32_t num_gids;
+ uint64_t *gids = NULL;
+ uint32_t num_sids;
+ struct dom_sid *sids = NULL;
+ bool guest;
+ PyObject *py_gids = NULL;
+ PyObject *py_sids = NULL;
+ PyObject *py_guest = NULL;
+ PyObject *py_ret = NULL;
+ Py_ssize_t i;
+
+ req = cli_posix_whoami_send(frame, self->ev, self->cli);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ goto fail;
+ }
+ status = cli_posix_whoami_recv(req,
+ frame,
+ &uid,
+ &gid,
+ &num_gids,
+ &gids,
+ &num_sids,
+ &sids,
+ &guest);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ goto fail;
+ }
+ if (num_gids > PY_SSIZE_T_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many GIDs");
+ goto fail;
+ }
+ if (num_sids > PY_SSIZE_T_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many SIDs");
+ goto fail;
+ }
+
+ py_gids = PyList_New(num_gids);
+ if (!py_gids) {
+ goto fail;
+ }
+ for (i = 0; i < num_gids; ++i) {
+ int ret;
+ PyObject *py_item = PyLong_FromUnsignedLongLong(gids[i]);
+ if (!py_item) {
+ goto fail2;
+ }
+
+ ret = PyList_SetItem(py_gids, i, py_item);
+ if (ret) {
+ goto fail2;
+ }
+ }
+ py_sids = PyList_New(num_sids);
+ if (!py_sids) {
+ goto fail2;
+ }
+ for (i = 0; i < num_sids; ++i) {
+ int ret;
+ struct dom_sid *sid;
+ PyObject *py_item;
+
+ sid = dom_sid_dup(frame, &sids[i]);
+ if (!sid) {
+ PyErr_NoMemory();
+ goto fail3;
+ }
+
+ py_item = pytalloc_steal(dom_sid_Type, sid);
+ if (!py_item) {
+ PyErr_NoMemory();
+ goto fail3;
+ }
+
+ ret = PyList_SetItem(py_sids, i, py_item);
+ if (ret) {
+ goto fail3;
+ }
+ }
+
+ py_guest = guest ? Py_True : Py_False;
+
+ py_ret = Py_BuildValue("KKNNO",
+ uid,
+ gid,
+ py_gids,
+ py_sids,
+ py_guest);
+ if (!py_ret) {
+ goto fail3;
+ }
+
+ TALLOC_FREE(frame);
+ return py_ret;
+
+fail3:
+ Py_CLEAR(py_sids);
+
+fail2:
+ Py_CLEAR(py_gids);
+
+fail:
+ TALLOC_FREE(frame);
+ return NULL;
+}
+
+/*
+ * Checks existence of a directory
+ */
+static bool check_dir_path(struct py_cli_state *self, const char *path)
+{
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+
+ req = cli_chkpath_send(NULL, self->ev, self->cli, path);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return false;
+ }
+ status = cli_chkpath_recv(req);
+ TALLOC_FREE(req);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args)
+{
+ const char *path = NULL;
+ bool dir_exists;
+
+ if (!PyArg_ParseTuple(args, "s:chkpath", &path)) {
+ return NULL;
+ }
+
+ dir_exists = check_dir_path(self, path);
+ return PyBool_FromLong(dir_exists);
+}
+
+static PyObject *py_smb_have_posix(struct py_cli_state *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ bool posix = smbXcli_conn_have_posix(self->cli->conn);
+
+ if (posix) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static PyObject *py_smb_protocol(struct py_cli_state *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ enum protocol_types proto = smbXcli_conn_protocol(self->cli->conn);
+ PyObject *result = PyLong_FromLong(proto);
+ return result;
+}
+
+static PyObject *py_smb_get_sd(struct py_cli_state *self, PyObject *args)
+{
+ int fnum;
+ unsigned sinfo;
+ struct tevent_req *req = NULL;
+ struct security_descriptor *sd = NULL;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) {
+ return NULL;
+ }
+
+ req = cli_query_security_descriptor_send(
+ NULL, self->ev, self->cli, fnum, sinfo);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_query_security_descriptor_recv(req, NULL, &sd);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ return py_return_ndr_struct(
+ "samba.dcerpc.security", "descriptor", sd, sd);
+}
+
+static PyObject *py_smb_set_sd(struct py_cli_state *self, PyObject *args)
+{
+ PyObject *py_sd = NULL;
+ struct tevent_req *req = NULL;
+ struct security_descriptor *sd = NULL;
+ uint16_t fnum;
+ unsigned int sinfo;
+ NTSTATUS status;
+
+ if (!PyArg_ParseTuple(args, "iOI:set_sd", &fnum, &py_sd, &sinfo)) {
+ return NULL;
+ }
+
+ sd = pytalloc_get_type(py_sd, struct security_descriptor);
+ if (!sd) {
+ PyErr_Format(PyExc_TypeError,
+ "Expected dcerpc.security.descriptor as argument, got %s",
+ pytalloc_get_name(py_sd));
+ return NULL;
+ }
+
+ req = cli_set_security_descriptor_send(
+ NULL, self->ev, self->cli, fnum, sinfo, sd);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+
+ status = cli_set_security_descriptor_recv(req);
+ PyErr_NTSTATUS_NOT_OK_RAISE(status);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_smb_smb1_posix(
+ struct py_cli_state *self, PyObject *Py_UNUSED(ignored))
+{
+ NTSTATUS status;
+ struct tevent_req *req = NULL;
+ uint16_t major, minor;
+ uint32_t caplow, caphigh;
+ PyObject *result = NULL;
+
+ req = cli_unix_extensions_version_send(NULL, self->ev, self->cli);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_unix_extensions_version_recv(
+ req, &major, &minor, &caplow, &caphigh);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ req = cli_set_unix_extensions_capabilities_send(
+ NULL, self->ev, self->cli, major, minor, caplow, caphigh);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_set_unix_extensions_capabilities_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ result = Py_BuildValue(
+ "[IIII]",
+ (unsigned)minor,
+ (unsigned)major,
+ (unsigned)caplow,
+ (unsigned)caphigh);
+ return result;
+}
+
+static PyObject *py_smb_smb1_readlink(
+ struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *filename = NULL;
+ struct tevent_req *req = NULL;
+ char *target = NULL;
+ PyObject *result = NULL;
+
+ if (!PyArg_ParseTuple(args, "s:smb1_readlink", &filename)) {
+ return NULL;
+ }
+
+ req = cli_posix_readlink_send(NULL, self->ev, self->cli, filename);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_posix_readlink_recv(req, NULL, &target);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ result = PyBytes_FromString(target);
+ TALLOC_FREE(target);
+ return result;
+}
+
+static PyObject *py_smb_smb1_symlink(
+ struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *target = NULL, *newname = NULL;
+ struct tevent_req *req = NULL;
+
+ if (!PyArg_ParseTuple(args, "ss:smb1_symlink", &target, &newname)) {
+ return NULL;
+ }
+
+ req = cli_posix_symlink_send(
+ NULL, self->ev, self->cli, target, newname);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_posix_symlink_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_smb_smb1_stat(
+ struct py_cli_state *self, PyObject *args)
+{
+ NTSTATUS status;
+ const char *fname = NULL;
+ struct tevent_req *req = NULL;
+ struct stat_ex sbuf = { .st_ex_nlink = 0, };
+
+ if (!PyArg_ParseTuple(args, "s:smb1_stat", &fname)) {
+ return NULL;
+ }
+
+ req = cli_posix_stat_send(NULL, self->ev, self->cli, fname);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_posix_stat_recv(req, &sbuf);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ return Py_BuildValue(
+ "{sLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLsL}",
+ "dev",
+ (unsigned long long)sbuf.st_ex_dev,
+ "ino",
+ (unsigned long long)sbuf.st_ex_ino,
+ "mode",
+ (unsigned long long)sbuf.st_ex_mode,
+ "nlink",
+ (unsigned long long)sbuf.st_ex_nlink,
+ "uid",
+ (unsigned long long)sbuf.st_ex_uid,
+ "gid",
+ (unsigned long long)sbuf.st_ex_gid,
+ "rdev",
+ (unsigned long long)sbuf.st_ex_size,
+ "atime_sec",
+ (unsigned long long)sbuf.st_ex_atime.tv_sec,
+ "atime_nsec",
+ (unsigned long long)sbuf.st_ex_atime.tv_nsec,
+ "mtime_sec",
+ (unsigned long long)sbuf.st_ex_mtime.tv_sec,
+ "mtime_nsec",
+ (unsigned long long)sbuf.st_ex_mtime.tv_nsec,
+ "ctime_sec",
+ (unsigned long long)sbuf.st_ex_ctime.tv_sec,
+ "ctime_nsec",
+ (unsigned long long)sbuf.st_ex_ctime.tv_nsec,
+ "btime_sec",
+ (unsigned long long)sbuf.st_ex_btime.tv_sec,
+ "btime_nsec",
+ (unsigned long long)sbuf.st_ex_btime.tv_nsec,
+ "cached_dos_attributes",
+ (unsigned long long)sbuf.cached_dos_attributes,
+ "blksize",
+ (unsigned long long)sbuf.st_ex_blksize,
+ "blocks",
+ (unsigned long long)sbuf.st_ex_blocks,
+ "flags",
+ (unsigned long long)sbuf.st_ex_flags,
+ "iflags",
+ (unsigned long long)sbuf.st_ex_iflags);
+}
+
+static PyObject *py_cli_mknod(
+ struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+ char *fname = NULL;
+ int mode = 0, major = 0, minor = 0, dev = 0;
+ struct tevent_req *req = NULL;
+ static const char *kwlist[] = {
+ "fname", "mode", "major", "minor", NULL,
+ };
+ NTSTATUS status;
+ bool ok;
+
+ ok = ParseTupleAndKeywords(
+ args,
+ kwds,
+ "sI|II:mknod",
+ kwlist,
+ &fname,
+ &mode,
+ &major,
+ &minor);
+ if (!ok) {
+ return NULL;
+ }
+
+#if defined(HAVE_MAKEDEV)
+ dev = makedev(major, minor);
+#endif
+
+ req = cli_mknod_send(
+ NULL, self->ev, self->cli, fname, mode, dev);
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+ status = cli_mknod_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_cli_fsctl(
+ struct py_cli_state *self, PyObject *args, PyObject *kwds)
+{
+ int fnum, ctl_code;
+ int max_out = 0;
+ char *buf = NULL;
+ Py_ssize_t buflen;
+ DATA_BLOB in = { .data = NULL, };
+ DATA_BLOB out = { .data = NULL, };
+ struct tevent_req *req = NULL;
+ PyObject *result = NULL;
+ static const char *kwlist[] = {
+ "fnum", "ctl_code", "in", "max_out", NULL,
+ };
+ NTSTATUS status;
+ bool ok;
+
+ ok = ParseTupleAndKeywords(
+ args,
+ kwds,
+ "ii" PYARG_BYTES_LEN "i",
+ kwlist,
+ &fnum,
+ &ctl_code,
+ &buf,
+ &buflen,
+ &max_out);
+ if (!ok) {
+ return NULL;
+ }
+
+ in = (DATA_BLOB) { .data = (uint8_t *)buf, .length = buflen, };
+
+ req = cli_fsctl_send(
+ NULL, self->ev, self->cli, fnum, ctl_code, &in, max_out);
+
+ if (!py_tevent_req_wait_exc(self, req)) {
+ return NULL;
+ }
+
+ status = cli_fsctl_recv(req, NULL, &out);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ return NULL;
+ }
+
+ result = PyBytes_FromStringAndSize((char *)out.data, out.length);
+ data_blob_free(&out);
+ return result;
+}
+
+static PyMethodDef py_cli_state_methods[] = {
+ { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS,
+ "settimeout(new_timeout_msecs) => return old_timeout_msecs" },
+ { "echo", (PyCFunction)py_cli_echo, METH_NOARGS,
+ "Ping the server connection" },
+ { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create),
+ METH_VARARGS|METH_KEYWORDS,
+ "Open a file" },
+ { "create_ex",
+ PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create_ex),
+ METH_VARARGS|METH_KEYWORDS,
+ "Open a file, SMB2 version returning create contexts" },
+ { "close", (PyCFunction)py_cli_close, METH_VARARGS,
+ "Close a file handle" },
+ { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write),
+ METH_VARARGS|METH_KEYWORDS,
+ "Write to a file handle" },
+ { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read),
+ METH_VARARGS|METH_KEYWORDS,
+ "Read from a file handle" },
+ { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_cli_ftruncate),
+ METH_VARARGS|METH_KEYWORDS,
+ "Truncate a file" },
+ { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_cli_delete_on_close),
+ METH_VARARGS|METH_KEYWORDS,
+ "Set/Reset the delete on close flag" },
+ { "notify", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_notify),
+ METH_VARARGS|METH_KEYWORDS,
+ "Wait for change notifications: \n"
+ "notify(fnum, buffer_size, completion_filter...) -> "
+ "libsmb_samba_internal.Notify request handle\n" },
+ { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list),
+ METH_VARARGS|METH_KEYWORDS,
+ "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> "
+ "directory contents as a dictionary\n"
+ "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | "
+ "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n"
+ "\t\tList contents of a directory. The keys are, \n"
+ "\t\t\tname: Long name of the directory item\n"
+ "\t\t\tshort_name: Short name of the directory item\n"
+ "\t\t\tsize: File size in bytes\n"
+ "\t\t\tattrib: Attributes\n"
+ "\t\t\tmtime: Modification time\n" },
+ { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break,
+ METH_VARARGS, "Wait for an oplock break" },
+ { "unlink", (PyCFunction)py_smb_unlink,
+ METH_VARARGS,
+ "unlink(path) -> None\n\n \t\tDelete a file." },
+ { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS,
+ "mkdir(path) -> None\n\n \t\tCreate a directory." },
+ { "posix_whoami", (PyCFunction)py_smb_posix_whoami, METH_NOARGS,
+ "posix_whoami() -> (uid, gid, gids, sids, guest)" },
+ { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS,
+ "rmdir(path) -> None\n\n \t\tDelete a directory." },
+ { "rename",
+ PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_rename),
+ METH_VARARGS|METH_KEYWORDS,
+ "rename(src,dst) -> None\n\n \t\tRename a file." },
+ { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS,
+ "chkpath(dir_path) -> True or False\n\n"
+ "\t\tReturn true if directory exists, false otherwise." },
+ { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS,
+ "savefile(path, bytes) -> None\n\n"
+ "\t\tWrite bytes to file." },
+ { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS,
+ "loadfile(path) -> file contents as a bytes object"
+ "\n\n\t\tRead contents of a file." },
+ { "get_sd", (PyCFunction)py_smb_get_sd, METH_VARARGS,
+ "get_sd(fnum[, security_info=0]) -> security_descriptor object\n\n"
+ "\t\tGet security descriptor for opened file." },
+ { "set_sd", (PyCFunction)py_smb_set_sd, METH_VARARGS,
+ "set_sd(fnum, security_descriptor[, security_info=0]) -> None\n\n"
+ "\t\tSet security descriptor for opened file." },
+ { "protocol",
+ (PyCFunction)py_smb_protocol,
+ METH_NOARGS,
+ "protocol() -> Number"
+ },
+ { "have_posix",
+ (PyCFunction)py_smb_have_posix,
+ METH_NOARGS,
+ "have_posix() -> True/False\n\n"
+ "\t\tReturn if the server has posix extensions"
+ },
+ { "smb1_posix",
+ (PyCFunction)py_smb_smb1_posix,
+ METH_NOARGS,
+ "Negotiate SMB1 posix extensions",
+ },
+ { "smb1_readlink",
+ (PyCFunction)py_smb_smb1_readlink,
+ METH_VARARGS,
+ "smb1_readlink(path) -> link target",
+ },
+ { "smb1_symlink",
+ (PyCFunction)py_smb_smb1_symlink,
+ METH_VARARGS,
+ "smb1_symlink(target, newname) -> None",
+ },
+ { "smb1_stat",
+ (PyCFunction)py_smb_smb1_stat,
+ METH_VARARGS,
+ "smb1_stat(path) -> stat info",
+ },
+ { "fsctl",
+ (PyCFunction)py_cli_fsctl,
+ METH_VARARGS|METH_KEYWORDS,
+ "fsctl(fnum, ctl_code, in_bytes, max_out) -> out_bytes",
+ },
+ { "mknod",
+ PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_mknod),
+ METH_VARARGS|METH_KEYWORDS,
+ "mknod(path, mode | major, minor)",
+ },
+ { NULL, NULL, 0, NULL }
+};
+
+static PyTypeObject py_cli_state_type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "libsmb_samba_cwrapper.LibsmbCConn",
+ .tp_basicsize = sizeof(struct py_cli_state),
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_doc = "libsmb cwrapper connection",
+ .tp_new = py_cli_state_new,
+ .tp_init = (initproc)py_cli_state_init,
+ .tp_dealloc = (destructor)py_cli_state_dealloc,
+ .tp_methods = py_cli_state_methods,
+};
+
+static PyMethodDef py_libsmb_methods[] = {
+ {0},
+};
+
+void initlibsmb_samba_cwrapper(void);
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "libsmb_samba_cwrapper",
+ .m_doc = "libsmb wrapper",
+ .m_size = -1,
+ .m_methods = py_libsmb_methods,
+};
+
+MODULE_INIT_FUNC(libsmb_samba_cwrapper)
+{
+ PyObject *m = NULL;
+ PyObject *mod = NULL;
+
+ talloc_stackframe();
+
+ if (PyType_Ready(&py_cli_state_type) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&py_cli_notify_state_type) < 0) {
+ return NULL;
+ }
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL) {
+ return m;
+ }
+
+ /* Import dom_sid type from dcerpc.security */
+ mod = PyImport_ImportModule("samba.dcerpc.security");
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ dom_sid_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "dom_sid");
+ if (dom_sid_Type == NULL) {
+ Py_DECREF(mod);
+ return NULL;
+ }
+
+ Py_INCREF(&py_cli_state_type);
+ PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type);
+
+#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val))
+
+ ADD_FLAGS(PROTOCOL_NONE);
+ ADD_FLAGS(PROTOCOL_CORE);
+ ADD_FLAGS(PROTOCOL_COREPLUS);
+ ADD_FLAGS(PROTOCOL_LANMAN1);
+ ADD_FLAGS(PROTOCOL_LANMAN2);
+ ADD_FLAGS(PROTOCOL_NT1);
+ ADD_FLAGS(PROTOCOL_SMB2_02);
+ ADD_FLAGS(PROTOCOL_SMB2_10);
+ ADD_FLAGS(PROTOCOL_SMB3_00);
+ ADD_FLAGS(PROTOCOL_SMB3_02);
+ ADD_FLAGS(PROTOCOL_SMB3_11);
+
+ ADD_FLAGS(FILE_ATTRIBUTE_READONLY);
+ ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN);
+ ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM);
+ ADD_FLAGS(FILE_ATTRIBUTE_VOLUME);
+ ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY);
+ ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE);
+ ADD_FLAGS(FILE_ATTRIBUTE_DEVICE);
+ ADD_FLAGS(FILE_ATTRIBUTE_NORMAL);
+ ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY);
+ ADD_FLAGS(FILE_ATTRIBUTE_SPARSE);
+ ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT);
+ ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED);
+ ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE);
+ ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED);
+ ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED);
+ ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK);
+
+ ADD_FLAGS(FILE_DIRECTORY_FILE);
+ ADD_FLAGS(FILE_WRITE_THROUGH);
+ ADD_FLAGS(FILE_SEQUENTIAL_ONLY);
+ ADD_FLAGS(FILE_NO_INTERMEDIATE_BUFFERING);
+ ADD_FLAGS(FILE_SYNCHRONOUS_IO_ALERT);
+ ADD_FLAGS(FILE_SYNCHRONOUS_IO_NONALERT);
+ ADD_FLAGS(FILE_NON_DIRECTORY_FILE);
+ ADD_FLAGS(FILE_CREATE_TREE_CONNECTION);
+ ADD_FLAGS(FILE_COMPLETE_IF_OPLOCKED);
+ ADD_FLAGS(FILE_NO_EA_KNOWLEDGE);
+ ADD_FLAGS(FILE_EIGHT_DOT_THREE_ONLY);
+ ADD_FLAGS(FILE_RANDOM_ACCESS);
+ ADD_FLAGS(FILE_DELETE_ON_CLOSE);
+ ADD_FLAGS(FILE_OPEN_BY_FILE_ID);
+ ADD_FLAGS(FILE_OPEN_FOR_BACKUP_INTENT);
+ ADD_FLAGS(FILE_NO_COMPRESSION);
+ ADD_FLAGS(FILE_RESERVER_OPFILTER);
+ ADD_FLAGS(FILE_OPEN_REPARSE_POINT);
+ ADD_FLAGS(FILE_OPEN_NO_RECALL);
+ ADD_FLAGS(FILE_OPEN_FOR_FREE_SPACE_QUERY);
+
+ ADD_FLAGS(FILE_SHARE_READ);
+ ADD_FLAGS(FILE_SHARE_WRITE);
+ ADD_FLAGS(FILE_SHARE_DELETE);
+
+ /* change notify completion filter flags */
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_FILE_NAME);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_DIR_NAME);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_ATTRIBUTES);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_SIZE);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_WRITE);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_ACCESS);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_CREATION);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_EA);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_SECURITY);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_NAME);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_SIZE);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_WRITE);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_NAME);
+ ADD_FLAGS(FILE_NOTIFY_CHANGE_ALL);
+
+ /* change notify action results */
+ ADD_FLAGS(NOTIFY_ACTION_ADDED);
+ ADD_FLAGS(NOTIFY_ACTION_REMOVED);
+ ADD_FLAGS(NOTIFY_ACTION_MODIFIED);
+ ADD_FLAGS(NOTIFY_ACTION_OLD_NAME);
+ ADD_FLAGS(NOTIFY_ACTION_NEW_NAME);
+ ADD_FLAGS(NOTIFY_ACTION_ADDED_STREAM);
+ ADD_FLAGS(NOTIFY_ACTION_REMOVED_STREAM);
+ ADD_FLAGS(NOTIFY_ACTION_MODIFIED_STREAM);
+
+ /* CreateDisposition values */
+ ADD_FLAGS(FILE_SUPERSEDE);
+ ADD_FLAGS(FILE_OPEN);
+ ADD_FLAGS(FILE_CREATE);
+ ADD_FLAGS(FILE_OPEN_IF);
+ ADD_FLAGS(FILE_OVERWRITE);
+ ADD_FLAGS(FILE_OVERWRITE_IF);
+
+ ADD_FLAGS(FSCTL_DFS_GET_REFERRALS);
+ ADD_FLAGS(FSCTL_DFS_GET_REFERRALS_EX);
+ ADD_FLAGS(FSCTL_REQUEST_OPLOCK_LEVEL_1);
+ ADD_FLAGS(FSCTL_REQUEST_OPLOCK_LEVEL_2);
+ ADD_FLAGS(FSCTL_REQUEST_BATCH_OPLOCK);
+ ADD_FLAGS(FSCTL_OPLOCK_BREAK_ACKNOWLEDGE);
+ ADD_FLAGS(FSCTL_OPBATCH_ACK_CLOSE_PENDING);
+ ADD_FLAGS(FSCTL_OPLOCK_BREAK_NOTIFY);
+ ADD_FLAGS(FSCTL_GET_COMPRESSION);
+ ADD_FLAGS(FSCTL_FILESYS_GET_STATISTICS);
+ ADD_FLAGS(FSCTL_GET_NTFS_VOLUME_DATA);
+ ADD_FLAGS(FSCTL_IS_VOLUME_DIRTY);
+ ADD_FLAGS(FSCTL_FIND_FILES_BY_SID);
+ ADD_FLAGS(FSCTL_SET_OBJECT_ID);
+ ADD_FLAGS(FSCTL_GET_OBJECT_ID);
+ ADD_FLAGS(FSCTL_DELETE_OBJECT_ID);
+ ADD_FLAGS(FSCTL_SET_REPARSE_POINT);
+ ADD_FLAGS(FSCTL_GET_REPARSE_POINT);
+ ADD_FLAGS(FSCTL_DELETE_REPARSE_POINT);
+ ADD_FLAGS(FSCTL_SET_OBJECT_ID_EXTENDED);
+ ADD_FLAGS(FSCTL_CREATE_OR_GET_OBJECT_ID);
+ ADD_FLAGS(FSCTL_SET_SPARSE);
+ ADD_FLAGS(FSCTL_SET_ZERO_DATA);
+ ADD_FLAGS(FSCTL_SET_ZERO_ON_DEALLOCATION);
+ ADD_FLAGS(FSCTL_READ_FILE_USN_DATA);
+ ADD_FLAGS(FSCTL_WRITE_USN_CLOSE_RECORD);
+ ADD_FLAGS(FSCTL_QUERY_ALLOCATED_RANGES);
+ ADD_FLAGS(FSCTL_QUERY_ON_DISK_VOLUME_INFO);
+ ADD_FLAGS(FSCTL_QUERY_SPARING_INFO);
+ ADD_FLAGS(FSCTL_FILE_LEVEL_TRIM);
+ ADD_FLAGS(FSCTL_OFFLOAD_READ);
+ ADD_FLAGS(FSCTL_OFFLOAD_WRITE);
+ ADD_FLAGS(FSCTL_SET_INTEGRITY_INFORMATION);
+ ADD_FLAGS(FSCTL_DUP_EXTENTS_TO_FILE);
+ ADD_FLAGS(FSCTL_DUPLICATE_EXTENTS_TO_FILE_EX);
+ ADD_FLAGS(FSCTL_STORAGE_QOS_CONTROL);
+ ADD_FLAGS(FSCTL_SVHDX_SYNC_TUNNEL_REQUEST);
+ ADD_FLAGS(FSCTL_QUERY_SHARED_VIRTUAL_DISK_SUPPORT);
+ ADD_FLAGS(FSCTL_PIPE_PEEK);
+ ADD_FLAGS(FSCTL_NAMED_PIPE_READ_WRITE);
+ ADD_FLAGS(FSCTL_PIPE_TRANSCEIVE);
+ ADD_FLAGS(FSCTL_PIPE_WAIT);
+ ADD_FLAGS(FSCTL_GET_SHADOW_COPY_DATA);
+ ADD_FLAGS(FSCTL_SRV_ENUM_SNAPS);
+ ADD_FLAGS(FSCTL_SRV_REQUEST_RESUME_KEY);
+ ADD_FLAGS(FSCTL_SRV_COPYCHUNK);
+ ADD_FLAGS(FSCTL_SRV_COPYCHUNK_WRITE);
+ ADD_FLAGS(FSCTL_SRV_READ_HASH);
+ ADD_FLAGS(FSCTL_LMR_REQ_RESILIENCY);
+ ADD_FLAGS(FSCTL_LMR_SET_LINK_TRACKING_INFORMATION);
+ ADD_FLAGS(FSCTL_QUERY_NETWORK_INTERFACE_INFO);
+
+ ADD_FLAGS(SYMLINK_ERROR_TAG);
+ ADD_FLAGS(SYMLINK_FLAG_RELATIVE);
+ ADD_FLAGS(SYMLINK_ADMIN);
+ ADD_FLAGS(SYMLINK_UNTRUSTED);
+ ADD_FLAGS(SYMLINK_TRUST_UNKNOWN);
+ ADD_FLAGS(SYMLINK_TRUST_MASK);
+
+ ADD_FLAGS(IO_REPARSE_TAG_RESERVED_ZERO);
+ ADD_FLAGS(IO_REPARSE_TAG_SYMLINK);
+ ADD_FLAGS(IO_REPARSE_TAG_MOUNT_POINT);
+ ADD_FLAGS(IO_REPARSE_TAG_HSM);
+ ADD_FLAGS(IO_REPARSE_TAG_SIS);
+ ADD_FLAGS(IO_REPARSE_TAG_DFS);
+ ADD_FLAGS(IO_REPARSE_TAG_NFS);
+
+#define ADD_STRING(val) PyModule_AddObject(m, #val, PyBytes_FromString(val))
+
+ ADD_STRING(SMB2_CREATE_TAG_EXTA);
+ ADD_STRING(SMB2_CREATE_TAG_MXAC);
+ ADD_STRING(SMB2_CREATE_TAG_SECD);
+ ADD_STRING(SMB2_CREATE_TAG_DHNQ);
+ ADD_STRING(SMB2_CREATE_TAG_DHNC);
+ ADD_STRING(SMB2_CREATE_TAG_ALSI);
+ ADD_STRING(SMB2_CREATE_TAG_TWRP);
+ ADD_STRING(SMB2_CREATE_TAG_QFID);
+ ADD_STRING(SMB2_CREATE_TAG_RQLS);
+ ADD_STRING(SMB2_CREATE_TAG_DH2Q);
+ ADD_STRING(SMB2_CREATE_TAG_DH2C);
+ ADD_STRING(SMB2_CREATE_TAG_AAPL);
+ ADD_STRING(SMB2_CREATE_TAG_APP_INSTANCE_ID);
+ ADD_STRING(SVHDX_OPEN_DEVICE_CONTEXT);
+ ADD_STRING(SMB2_CREATE_TAG_POSIX);
+ ADD_FLAGS(SMB2_FIND_POSIX_INFORMATION);
+ ADD_FLAGS(FILE_SUPERSEDE);
+ ADD_FLAGS(FILE_OPEN);
+ ADD_FLAGS(FILE_CREATE);
+ ADD_FLAGS(FILE_OPEN_IF);
+ ADD_FLAGS(FILE_OVERWRITE);
+ ADD_FLAGS(FILE_OVERWRITE_IF);
+ ADD_FLAGS(FILE_DIRECTORY_FILE);
+
+ ADD_FLAGS(SMB2_CLOSE_FLAGS_FULL_INFORMATION);
+
+ return m;
+}
diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c
new file mode 100644
index 0000000..ab81b43
--- /dev/null
+++ b/source3/libsmb/samlogon_cache.c
@@ -0,0 +1,405 @@
+/*
+ Unix SMB/CIFS implementation.
+ Net_sam_logon info3 helpers
+ Copyright (C) Alexander Bokovoy 2002.
+ Copyright (C) Andrew Bartlett 2002.
+ Copyright (C) Gerald Carter 2003.
+ Copyright (C) Tim Potter 2003.
+ Copyright (C) Guenther Deschner 2008.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "samlogon_cache.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "lib/util/debug.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/util/memory.h" /* for SAFE_FREE() */
+#include "source3/lib/util_path.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "../libcli/security/security.h"
+#include "util_tdb.h"
+
+#define NETSAMLOGON_TDB "netsamlogon_cache.tdb"
+
+static TDB_CONTEXT *netsamlogon_tdb = NULL;
+
+/***********************************************************************
+ open the tdb
+ ***********************************************************************/
+
+bool netsamlogon_cache_init(void)
+{
+ bool first_try = true;
+ char *path = NULL;
+ int ret;
+ struct tdb_context *tdb;
+
+ if (netsamlogon_tdb) {
+ return true;
+ }
+
+ path = cache_path(talloc_tos(), NETSAMLOGON_TDB);
+ if (path == NULL) {
+ return false;
+ }
+again:
+ tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH,
+ O_RDWR | O_CREAT, 0600);
+ if (tdb == NULL) {
+ DEBUG(0,("tdb_open_log('%s') - failed\n", path));
+ goto clear;
+ }
+
+ ret = tdb_check(tdb, NULL, NULL);
+ if (ret != 0) {
+ tdb_close(tdb);
+ DEBUG(0,("tdb_check('%s') - failed\n", path));
+ goto clear;
+ }
+
+ netsamlogon_tdb = tdb;
+ talloc_free(path);
+ return true;
+
+clear:
+ if (!first_try) {
+ talloc_free(path);
+ return false;
+ }
+ first_try = false;
+
+ DEBUG(0,("retry after truncate for '%s'\n", path));
+ ret = truncate(path, 0);
+ if (ret == -1) {
+ DBG_ERR("truncate failed: %s\n", strerror(errno));
+ talloc_free(path);
+ return false;
+ }
+
+ goto again;
+}
+
+/***********************************************************************
+ Clear cache getpwnam and getgroups entries from the winbindd cache
+***********************************************************************/
+
+void netsamlogon_clear_cached_user(const struct dom_sid *user_sid)
+{
+ struct dom_sid_buf keystr;
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_clear_cached_user: cannot open "
+ "%s for write!\n",
+ NETSAMLOGON_TDB));
+ return;
+ }
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ dom_sid_str_buf(user_sid, &keystr);
+
+ DBG_DEBUG("SID [%s]\n", keystr.buf);
+
+ tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
+}
+
+/***********************************************************************
+ Store a netr_SamInfo3 structure in a tdb for later user
+ username should be in UTF-8 format
+***********************************************************************/
+
+bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3)
+{
+ uint8_t dummy = 0;
+ TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) };
+ struct dom_sid_buf keystr;
+ bool result = false;
+ struct dom_sid user_sid;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ struct netsamlogoncache_entry r;
+ int ret;
+
+ if (!info3) {
+ goto fail;
+ }
+
+ if (!netsamlogon_cache_init()) {
+ D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n",
+ NETSAMLOGON_TDB);
+ goto fail;
+ }
+
+ /*
+ * First write a record with just the domain sid for
+ * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid
+ * overwriting potentially other data. We're just interested
+ * in the existence of that record.
+ */
+ dom_sid_str_buf(info3->base.domain_sid, &keystr);
+
+ ret = tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_INSERT);
+
+ if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) {
+ D_WARNING("Could not store domain marker for %s: %s\n",
+ keystr.buf, tdb_errorstr(netsamlogon_tdb));
+ goto fail;
+ }
+
+ sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid);
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ dom_sid_str_buf(&user_sid, &keystr);
+
+ DBG_DEBUG("SID [%s]\n", keystr.buf);
+
+ /* Prepare data */
+
+ if (info3->base.full_name.string == NULL) {
+ struct netr_SamInfo3 *cached_info3;
+ const char *full_name = NULL;
+
+ cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid);
+ if (cached_info3 != NULL) {
+ full_name = cached_info3->base.full_name.string;
+ }
+
+ if (full_name != NULL) {
+ info3->base.full_name.string = talloc_strdup(info3, full_name);
+ if (info3->base.full_name.string == NULL) {
+ goto fail;
+ }
+ }
+ }
+
+ /* only Samba fills in the username, not sure why NT doesn't */
+ /* so we fill it in since winbindd_getpwnam() makes use of it */
+
+ if (!info3->base.account_name.string) {
+ info3->base.account_name.string = talloc_strdup(info3, username);
+ if (info3->base.account_name.string == NULL) {
+ goto fail;
+ }
+ }
+
+ r.timestamp = time(NULL);
+ r.info3 = *info3;
+
+ /* avoid storing secret information */
+ ZERO_STRUCT(r.info3.base.key);
+ ZERO_STRUCT(r.info3.base.LMSessKey);
+
+ if (DEBUGLEVEL >= 10) {
+ NDR_PRINT_DEBUG(netsamlogoncache_entry, &r);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r,
+ (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("failed to push entry to cache: %s\n",
+ ndr_errstr(ndr_err));
+ goto fail;
+ }
+
+ data.dsize = blob.length;
+ data.dptr = blob.data;
+
+ if (tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_REPLACE) == 0) {
+ result = true;
+ }
+
+fail:
+ TALLOC_FREE(tmp_ctx);
+ return result;
+}
+
+/***********************************************************************
+ Retrieves a netr_SamInfo3 structure from a tdb. Caller must
+ free the user_info struct (talloced memory)
+***********************************************************************/
+
+struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid)
+{
+ struct netr_SamInfo3 *info3 = NULL;
+ TDB_DATA data;
+ struct dom_sid_buf keystr;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct netsamlogoncache_entry r;
+
+ if (!netsamlogon_cache_init()) {
+ DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n",
+ NETSAMLOGON_TDB));
+ return NULL;
+ }
+
+ /* Prepare key as DOMAIN-SID/USER-RID string */
+ dom_sid_str_buf(user_sid, &keystr);
+ DBG_DEBUG("SID [%s]\n", keystr.buf);
+ data = tdb_fetch_bystring( netsamlogon_tdb, keystr.buf );
+
+ if (!data.dptr) {
+ D_DEBUG("tdb fetch for %s is empty\n", keystr.buf);
+ return NULL;
+ }
+
+ info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
+ if (!info3) {
+ goto done;
+ }
+
+ blob = data_blob_const(data.dptr, data.dsize);
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n");
+ tdb_delete_bystring(netsamlogon_tdb, keystr.buf);
+ TALLOC_FREE(info3);
+ goto done;
+ }
+
+ NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, netsamlogoncache_entry, &r);
+
+ info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3,
+ sizeof(r.info3));
+
+ done:
+ SAFE_FREE(data.dptr);
+
+ return info3;
+}
+
+bool netsamlogon_cache_have(const struct dom_sid *sid)
+{
+ struct dom_sid_buf keystr;
+ bool ok;
+
+ if (!netsamlogon_cache_init()) {
+ DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB);
+ return false;
+ }
+
+ dom_sid_str_buf(sid, &keystr);
+
+ ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr.buf));
+ return ok;
+}
+
+struct netsamlog_cache_forall_state {
+ TALLOC_CTX *mem_ctx;
+ int (*cb)(const char *sid_str,
+ time_t when_cached,
+ struct netr_SamInfo3 *,
+ void *private_data);
+ void *private_data;
+};
+
+static int netsamlog_cache_traverse_cb(struct tdb_context *tdb,
+ TDB_DATA key,
+ TDB_DATA data,
+ void *private_data)
+{
+ struct netsamlog_cache_forall_state *state =
+ (struct netsamlog_cache_forall_state *)private_data;
+ TALLOC_CTX *mem_ctx = NULL;
+ DATA_BLOB blob;
+ const char *sid_str = NULL;
+ struct dom_sid sid;
+ struct netsamlogoncache_entry r;
+ enum ndr_err_code ndr_err;
+ int ret;
+ bool ok;
+
+ if (key.dsize == 0) {
+ return 0;
+ }
+ if (key.dptr[key.dsize - 1] != '\0') {
+ return 0;
+ }
+ if (data.dptr == NULL) {
+ return 0;
+ }
+ sid_str = (char *)key.dptr;
+
+ ok = string_to_sid(&sid, sid_str);
+ if (!ok) {
+ DBG_ERR("String to SID failed for %s\n", sid_str);
+ return -1;
+ }
+
+ if (sid.num_auths != 5) {
+ return 0;
+ }
+
+ mem_ctx = talloc_new(state->mem_ctx);
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ blob = data_blob_const(data.dptr, data.dsize);
+
+ ndr_err = ndr_pull_struct_blob(
+ &blob, state->mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_ERR("failed to pull entry from cache\n");
+ return -1;
+ }
+
+ ret = state->cb(sid_str, r.timestamp, &r.info3, state->private_data);
+
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+int netsamlog_cache_for_all(int (*cb)(const char *sid_str,
+ time_t when_cached,
+ struct netr_SamInfo3 *,
+ void *private_data),
+ void *private_data)
+{
+ int ret;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct netsamlog_cache_forall_state state;
+
+ if (!netsamlogon_cache_init()) {
+ DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB);
+ return -1;
+ }
+
+ mem_ctx = talloc_init("netsamlog_cache_for_all");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ state = (struct netsamlog_cache_forall_state) {
+ .mem_ctx = mem_ctx,
+ .cb = cb,
+ .private_data = private_data,
+ };
+
+ ret = tdb_traverse_read(netsamlogon_tdb,
+ netsamlog_cache_traverse_cb,
+ &state);
+
+ TALLOC_FREE(state.mem_ctx);
+ return ret;
+}
diff --git a/source3/libsmb/samlogon_cache.h b/source3/libsmb/samlogon_cache.h
new file mode 100644
index 0000000..29e0cea
--- /dev/null
+++ b/source3/libsmb/samlogon_cache.h
@@ -0,0 +1,46 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Net_sam_logon info3 helpers
+ * Copyright (C) Alexander Bokovoy 2002.
+ * Copyright (C) Andrew Bartlett 2002.
+ * Copyright (C) Gerald Carter 2003.
+ * Copyright (C) Tim Potter 2003.
+ * Copyright (C) Guenther Deschner 2008.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMB_SAMLOGON_CACHE_H__
+#define __LIBSMB_SAMLOGON_CACHE_H__
+
+#include "replace.h"
+#include <talloc.h>
+
+struct dom_sid;
+struct netr_SamInfo3;
+
+bool netsamlogon_cache_init(void);
+void netsamlogon_clear_cached_user(const struct dom_sid *user_sid);
+bool netsamlogon_cache_store(const char *username,
+ struct netr_SamInfo3 *info3);
+struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *user_sid);
+bool netsamlogon_cache_have(const struct dom_sid *sid);
+int netsamlog_cache_for_all(int (*cb)(const char *sid_str,
+ time_t when_cached,
+ struct netr_SamInfo3 *,
+ void *private_data),
+ void *private_data);
+
+#endif
diff --git a/source3/libsmb/smbclient.pc.in b/source3/libsmb/smbclient.pc.in
new file mode 100644
index 0000000..bcef2f2
--- /dev/null
+++ b/source3/libsmb/smbclient.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: smbclient
+Description: A SMB library interface
+Version: @PACKAGE_VERSION@
+Libs: @LIB_RPATH@ -L${libdir} -lsmbclient
+Cflags: -I${includedir}
+URL: http://www.samba.org/
diff --git a/source3/libsmb/smberr.c b/source3/libsmb/smberr.c
new file mode 100644
index 0000000..167c796
--- /dev/null
+++ b/source3/libsmb/smberr.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Andrew Tridgell 1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+/* error code stuff - put together by Merik Karman
+ merik@blackadder.dsh.oz.au */
+
+
+/* There is a big list of error codes and their meanings at:
+
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/errlist_7oz7.asp
+
+ and if you don't like MSDN try:
+
+ http://www.siris.gr/computers/library/error.htm
+
+*/
+
+typedef const struct
+{
+ const char *name;
+ int code;
+ const char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+static err_code_struct dos_msgs[] = {
+ {"ERRbadfunc",ERRbadfunc,"Invalid function."},
+ {"ERRbadfile",ERRbadfile,"File not found."},
+ {"ERRbadpath",ERRbadpath,"Directory invalid."},
+ {"ERRnofids",ERRnofids,"No file descriptors available"},
+ {"ERRnoaccess",ERRnoaccess,"Access denied."},
+ {"ERRbadfid",ERRbadfid,"Invalid file handle."},
+ {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."},
+ {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."},
+ {"ERRbadmem",ERRbadmem,"Invalid memory block address."},
+ {"ERRbadenv",ERRbadenv,"Invalid environment."},
+ {"ERRbadformat",11,"Invalid format."},
+ {"ERRbadaccess",ERRbadaccess,"Invalid open mode."},
+ {"ERRbaddata",ERRbaddata,"Invalid data."},
+ {"ERRres",ERRres,"reserved."},
+ {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."},
+ {"ERRremcd",ERRremcd,"A Delete Directory request attempted to remove the server's current directory."},
+ {"ERRdiffdevice",ERRdiffdevice,"Not same device."},
+ {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."},
+ {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing FIDs on the file."},
+ {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRunsup", ERRunsup, "The operation is unsupported"},
+ {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"},
+ {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make New File or Link request already exists."},
+ {"ERRinvalidname",ERRinvalidname, "Invalid name"},
+ {"ERRbadpipe",ERRbadpipe,"Pipe invalid."},
+ {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."},
+ {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."},
+ {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."},
+ {"ERRmoredata",ERRmoredata,"There is more data to be returned."},
+ {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"},
+ {"ERRlogonfailure",ERRlogonfailure,"Logon failure"},
+ {"ERRdiskfull",ERRdiskfull,"Disk full"},
+ {"ERRgeneral",ERRgeneral, "General failure"},
+ {"ERRbaddirectory", ERRbaddirectory, "Bad directory name"},
+ {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"},
+ {NULL,-1,NULL}};
+
+/* Server Error Messages */
+static err_code_struct server_msgs[] = {
+ {"ERRerror",1,"Non-specific error code."},
+ {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+ {"ERRbadtype",3,"reserved."},
+ {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."},
+ {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+ {"ERRinvnetname",6,"Invalid network name in tree connect."},
+ {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."},
+ {"ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+ {"ERRqtoobig",50,"Print queue full -- no space."},
+ {"ERRqeof",51,"EOF on print queue dump."},
+ {"ERRinvpfid",52,"Invalid print file FID."},
+ {"ERRsmbcmd",64,"The server did not recognize the command received."},
+ {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+ {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."},
+ {"ERRreserved",68,"reserved."},
+ {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."},
+ {"ERRreserved",70,"reserved."},
+ {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+ {"ERRpaused",81,"Server is paused."},
+ {"ERRmsgoff",82,"Not receiving messages."},
+ {"ERRnoroom",83,"No room to buffer message."},
+ {"ERRrmuns",87,"Too many remote user names."},
+ {"ERRtimeout",88,"Operation timed out."},
+ {"ERRnoresource",89,"No resources currently available for request."},
+ {"ERRtoomanyuids",90,"Too many UIDs active on this session."},
+ {"ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+ {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+ {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+ {"ERRcontmpx",252,"Continue in MPX mode."},
+ {"ERRreserved",253,"reserved."},
+ {"ERRreserved",254,"reserved."},
+ {"ERRnosupport",0xFFFF,"Function not supported."},
+ {NULL,-1,NULL}};
+
+/* Hard Error Messages */
+static err_code_struct hard_msgs[] = {
+ {"ERRnowrite",19,"Attempt to write on write-protected diskette."},
+ {"ERRbadunit",20,"Unknown unit."},
+ {"ERRnotready",21,"Drive not ready."},
+ {"ERRbadcmd",22,"Unknown command."},
+ {"ERRdata",23,"Data error (CRC)."},
+ {"ERRbadreq",24,"Bad request structure length."},
+ {"ERRseek",25 ,"Seek error."},
+ {"ERRbadmedia",26,"Unknown media type."},
+ {"ERRbadsector",27,"Sector not found."},
+ {"ERRnopaper",28,"Printer out of paper."},
+ {"ERRwrite",29,"Write fault."},
+ {"ERRread",30,"Read fault."},
+ {"ERRgeneral",31,"General failure."},
+ {"ERRbadshare",32,"An open conflicts with an existing open."},
+ {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+ {"ERRwrongdisk",34,"The wrong disk was found in a drive."},
+ {"ERRFCBUnavail",35,"No FCBs are available to process request."},
+ {"ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+ {NULL,-1,NULL}};
+
+
+static const struct
+{
+ int code;
+ const char *e_class;
+ err_code_struct *err_msgs;
+} err_classes[] = {
+ {0,"SUCCESS",NULL},
+ {0x01,"ERRDOS",dos_msgs},
+ {0x02,"ERRSRV",server_msgs},
+ {0x03,"ERRHRD",hard_msgs},
+ {0x04,"ERRXOS",NULL},
+ {0xE1,"ERRRMX1",NULL},
+ {0xE2,"ERRRMX2",NULL},
+ {0xE3,"ERRRMX3",NULL},
+ {0xFF,"ERRCMD",NULL},
+ {-1,NULL,NULL}};
+
+
+/****************************************************************************
+return a SMB error name from a class and code
+****************************************************************************/
+const char *smb_dos_err_name(uint8_t e_class, uint16_t num)
+{
+ char *result;
+ int i,j;
+
+ for (i=0;err_classes[i].e_class;i++)
+ if (err_classes[i].code == e_class) {
+ if (err_classes[i].err_msgs) {
+ err_code_struct *err = err_classes[i].err_msgs;
+ for (j=0;err[j].name;j++)
+ if (num == err[j].code) {
+ return err[j].name;
+ }
+ }
+ result = talloc_asprintf(talloc_tos(), "%d", num);
+ SMB_ASSERT(result != NULL);
+ return result;
+ }
+
+ result = talloc_asprintf(talloc_tos(), "Error: Unknown error class "
+ "(%d,%d)", e_class,num);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/* Return a string for a DOS error */
+
+const char *get_dos_error_msg(WERROR result)
+{
+ uint16_t errnum;
+
+ errnum = W_ERROR_V(result);
+
+ return smb_dos_err_name(ERRDOS, errnum);
+}
+
+/****************************************************************************
+return a SMB error class name as a string.
+****************************************************************************/
+const char *smb_dos_err_class(uint8_t e_class)
+{
+ char *result;
+ int i;
+
+ for (i=0;err_classes[i].e_class;i++) {
+ if (err_classes[i].code == e_class) {
+ return err_classes[i].e_class;
+ }
+ }
+
+ result = talloc_asprintf(talloc_tos(), "Error: Unknown class (%d)",
+ e_class);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/*****************************************************************************
+map a unix errno to a win32 error
+ *****************************************************************************/
+WERROR map_werror_from_unix(int error)
+{
+ NTSTATUS status = map_nt_error_from_unix(error);
+ return ntstatus_to_werror(status);
+}
diff --git a/source3/libsmb/smbsock_connect.c b/source3/libsmb/smbsock_connect.c
new file mode 100644
index 0000000..7f0baad
--- /dev/null
+++ b/source3/libsmb/smbsock_connect.c
@@ -0,0 +1,870 @@
+/*
+ Unix SMB/CIFS implementation.
+ Connect to 445 and 139/nbsesssetup
+ Copyright (C) Volker Lendecke 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/async_req/async_sock.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "../lib/util/tevent_unix.h"
+#include "client.h"
+#include "async_smb.h"
+#include "../libcli/smb/read_smb.h"
+#include "libsmb/nmblib.h"
+
+struct cli_session_request_state {
+ struct tevent_context *ev;
+ int sock;
+ uint32_t len_hdr;
+ struct iovec iov[3];
+ uint8_t nb_session_response;
+};
+
+static void cli_session_request_sent(struct tevent_req *subreq);
+static void cli_session_request_recvd(struct tevent_req *subreq);
+
+static struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int sock,
+ const struct nmb_name *called,
+ const struct nmb_name *calling)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_session_request_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->sock = sock;
+
+ state->iov[1].iov_base = name_mangle(
+ state, called->name, called->name_type);
+ if (tevent_req_nomem(state->iov[1].iov_base, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->iov[1].iov_len = name_len(
+ (unsigned char *)state->iov[1].iov_base,
+ talloc_get_size(state->iov[1].iov_base));
+
+ state->iov[2].iov_base = name_mangle(
+ state, calling->name, calling->name_type);
+ if (tevent_req_nomem(state->iov[2].iov_base, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->iov[2].iov_len = name_len(
+ (unsigned char *)state->iov[2].iov_base,
+ talloc_get_size(state->iov[2].iov_base));
+
+ _smb_setlen(((char *)&state->len_hdr),
+ state->iov[1].iov_len + state->iov[2].iov_len);
+ SCVAL((char *)&state->len_hdr, 0, 0x81);
+
+ state->iov[0].iov_base = &state->len_hdr;
+ state->iov[0].iov_len = sizeof(state->len_hdr);
+
+ subreq = writev_send(state, ev, NULL, sock, true, state->iov, 3);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_request_sent, req);
+ return req;
+}
+
+static void cli_session_request_sent(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+ ssize_t ret;
+ int err;
+
+ ret = writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ subreq = read_smb_send(state, state->ev, state->sock);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_session_request_recvd, req);
+}
+
+static void cli_session_request_recvd(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+ uint8_t *buf;
+ ssize_t ret;
+ int err;
+
+ ret = read_smb_recv(subreq, talloc_tos(), &buf, &err);
+ TALLOC_FREE(subreq);
+
+ if (ret < 4) {
+ ret = -1;
+ err = EIO;
+ }
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ /*
+ * In case of an error there is more information in the data
+ * portion according to RFC1002. We're not subtle enough to
+ * respond to the different error conditions, so drop the
+ * error info here.
+ */
+ state->nb_session_response = CVAL(buf, 0);
+ tevent_req_done(req);
+}
+
+static bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp)
+{
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return false;
+ }
+ *resp = state->nb_session_response;
+ return true;
+}
+
+struct nb_connect_state {
+ struct tevent_context *ev;
+ const struct sockaddr_storage *addr;
+ const char *called_name;
+ int sock;
+ struct tevent_req *session_subreq;
+ struct nmb_name called;
+ struct nmb_name calling;
+};
+
+static void nb_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+static void nb_connect_connected(struct tevent_req *subreq);
+static void nb_connect_done(struct tevent_req *subreq);
+
+static struct tevent_req *nb_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct sockaddr_storage *addr,
+ const char *called_name,
+ int called_type,
+ const char *calling_name,
+ int calling_type)
+{
+ struct tevent_req *req, *subreq;
+ struct nb_connect_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct nb_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->called_name = called_name;
+ state->addr = addr;
+
+ state->sock = -1;
+ make_nmb_name(&state->called, called_name, called_type);
+ make_nmb_name(&state->calling, calling_name, calling_type);
+
+ tevent_req_set_cleanup_fn(req, nb_connect_cleanup);
+
+ subreq = open_socket_out_send(state, ev, addr, NBT_SMB_PORT, 5000);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nb_connect_connected, req);
+ return req;
+}
+
+static void nb_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct nb_connect_state *state = tevent_req_data(
+ req, struct nb_connect_state);
+
+ /*
+ * we need to free a pending request before closing the
+ * socket, see bug #11141
+ */
+ TALLOC_FREE(state->session_subreq);
+
+ if (req_state == TEVENT_REQ_DONE) {
+ /*
+ * we keep the socket open for the caller to use
+ */
+ return;
+ }
+
+ if (state->sock != -1) {
+ close(state->sock);
+ state->sock = -1;
+ }
+
+ return;
+}
+
+static void nb_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_connect_state *state = tevent_req_data(
+ req, struct nb_connect_state);
+ NTSTATUS status;
+
+ status = open_socket_out_recv(subreq, &state->sock);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ subreq = cli_session_request_send(state, state->ev, state->sock,
+ &state->called, &state->calling);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_connect_done, req);
+ state->session_subreq = subreq;
+}
+
+static void nb_connect_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_connect_state *state = tevent_req_data(
+ req, struct nb_connect_state);
+ bool ret;
+ int err;
+ uint8_t resp;
+
+ state->session_subreq = NULL;
+
+ ret = cli_session_request_recv(subreq, &err, &resp);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ /*
+ * RFC1002: 0x82 - POSITIVE SESSION RESPONSE
+ */
+
+ if (resp != 0x82) {
+ /*
+ * The server did not like our session request
+ */
+ close(state->sock);
+ state->sock = -1;
+
+ if (strequal(state->called_name, "*SMBSERVER")) {
+ /*
+ * Here we could try a name status request and
+ * use the first 0x20 type name.
+ */
+ tevent_req_nterror(
+ req, NT_STATUS_RESOURCE_NAME_NOT_FOUND);
+ return;
+ }
+
+ /*
+ * We could be subtle and distinguish between
+ * different failure modes, but what we do here
+ * instead is just retry with *SMBSERVER type 0x20.
+ */
+ state->called_name = "*SMBSERVER";
+ make_nmb_name(&state->called, state->called_name, 0x20);
+
+ subreq = open_socket_out_send(state, state->ev, state->addr,
+ NBT_SMB_PORT, 5000);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_connect_connected, req);
+ return;
+ }
+
+ tevent_req_done(req);
+ return;
+}
+
+static NTSTATUS nb_connect_recv(struct tevent_req *req, int *sock)
+{
+ struct nb_connect_state *state = tevent_req_data(
+ req, struct nb_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *sock = state->sock;
+ state->sock = -1;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct smbsock_connect_state {
+ struct tevent_context *ev;
+ const struct sockaddr_storage *addr;
+ const char *called_name;
+ uint8_t called_type;
+ const char *calling_name;
+ uint8_t calling_type;
+ struct tevent_req *req_139;
+ struct tevent_req *req_445;
+ int sock;
+ uint16_t port;
+};
+
+static void smbsock_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+static void smbsock_connect_connected(struct tevent_req *subreq);
+static void smbsock_connect_do_139(struct tevent_req *subreq);
+
+struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct sockaddr_storage *addr,
+ uint16_t port,
+ const char *called_name,
+ int called_type,
+ const char *calling_name,
+ int calling_type)
+{
+ struct tevent_req *req;
+ struct smbsock_connect_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct smbsock_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->addr = addr;
+ state->sock = -1;
+ state->called_name =
+ (called_name != NULL) ? called_name : "*SMBSERVER";
+ state->called_type =
+ (called_type != -1) ? called_type : 0x20;
+ state->calling_name =
+ (calling_name != NULL) ? calling_name : lp_netbios_name();
+ state->calling_type =
+ (calling_type != -1) ? calling_type : 0x00;
+
+ tevent_req_set_cleanup_fn(req, smbsock_connect_cleanup);
+
+ if (port == NBT_SMB_PORT) {
+ if (lp_disable_netbios()) {
+ tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+ return tevent_req_post(req, ev);
+ }
+
+ state->req_139 = nb_connect_send(state, state->ev, state->addr,
+ state->called_name,
+ state->called_type,
+ state->calling_name,
+ state->calling_type);
+ if (tevent_req_nomem(state->req_139, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->req_139, smbsock_connect_connected, req);
+ return req;
+ }
+ if (port != 0) {
+ state->req_445 = open_socket_out_send(state, ev, addr, port,
+ 5000);
+ if (tevent_req_nomem(state->req_445, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(
+ state->req_445, smbsock_connect_connected, req);
+ return req;
+ }
+
+ /*
+ * port==0, try both
+ */
+
+ state->req_445 = open_socket_out_send(state, ev, addr, TCP_SMB_PORT, 5000);
+ if (tevent_req_nomem(state->req_445, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->req_445, smbsock_connect_connected,
+ req);
+
+ /*
+ * Check for disable_netbios
+ */
+ if (lp_disable_netbios()) {
+ return req;
+ }
+
+ /*
+ * After 5 msecs, fire the 139 (NBT) request
+ */
+ state->req_139 = tevent_wakeup_send(
+ state, ev, timeval_current_ofs(0, 5000));
+ if (tevent_req_nomem(state->req_139, req)) {
+ TALLOC_FREE(state->req_445);
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(state->req_139, smbsock_connect_do_139,
+ req);
+ return req;
+}
+
+static void smbsock_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct smbsock_connect_state *state = tevent_req_data(
+ req, struct smbsock_connect_state);
+
+ /*
+ * we need to free a pending request before closing the
+ * socket, see bug #11141
+ */
+ TALLOC_FREE(state->req_445);
+ TALLOC_FREE(state->req_139);
+
+ if (req_state == TEVENT_REQ_DONE) {
+ /*
+ * we keep the socket open for the caller to use
+ */
+ return;
+ }
+
+ if (state->sock != -1) {
+ close(state->sock);
+ state->sock = -1;
+ }
+
+ return;
+}
+
+static void smbsock_connect_do_139(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbsock_connect_state *state = tevent_req_data(
+ req, struct smbsock_connect_state);
+ bool ret;
+
+ ret = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ state->req_139 = nb_connect_send(state, state->ev, state->addr,
+ state->called_name,
+ state->called_type,
+ state->calling_name,
+ state->calling_type);
+ if (tevent_req_nomem(state->req_139, req)) {
+ return;
+ }
+ tevent_req_set_callback(state->req_139, smbsock_connect_connected,
+ req);
+}
+
+static void smbsock_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbsock_connect_state *state = tevent_req_data(
+ req, struct smbsock_connect_state);
+ struct tevent_req *unfinished_req;
+ NTSTATUS status;
+
+ if (subreq == state->req_445) {
+
+ status = open_socket_out_recv(subreq, &state->sock);
+ TALLOC_FREE(state->req_445);
+ unfinished_req = state->req_139;
+ state->port = TCP_SMB_PORT;
+
+ } else if (subreq == state->req_139) {
+
+ status = nb_connect_recv(subreq, &state->sock);
+ TALLOC_FREE(state->req_139);
+ unfinished_req = state->req_445;
+ state->port = NBT_SMB_PORT;
+
+ } else {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(unfinished_req);
+ state->req_139 = NULL;
+ state->req_445 = NULL;
+ tevent_req_done(req);
+ return;
+ }
+ if (unfinished_req == NULL) {
+ /*
+ * Both requests failed
+ */
+ tevent_req_nterror(req, status);
+ return;
+ }
+ /*
+ * Do nothing, wait for the second request to come here.
+ */
+}
+
+NTSTATUS smbsock_connect_recv(struct tevent_req *req, int *sock,
+ uint16_t *ret_port)
+{
+ struct smbsock_connect_state *state = tevent_req_data(
+ req, struct smbsock_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *sock = state->sock;
+ state->sock = -1;
+ if (ret_port != NULL) {
+ *ret_port = state->port;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsock_connect(const struct sockaddr_storage *addr, uint16_t port,
+ const char *called_name, int called_type,
+ const char *calling_name, int calling_type,
+ int *pfd, uint16_t *ret_port, int sec_timeout)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = smbsock_connect_send(frame, ev, addr, port,
+ called_name, called_type,
+ calling_name, calling_type);
+ if (req == NULL) {
+ goto fail;
+ }
+ if ((sec_timeout != 0) &&
+ !tevent_req_set_endtime(
+ req, ev, timeval_current_ofs(sec_timeout, 0))) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = smbsock_connect_recv(req, pfd, ret_port);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct smbsock_any_connect_state {
+ struct tevent_context *ev;
+ const struct sockaddr_storage *addrs;
+ const char **called_names;
+ int *called_types;
+ const char **calling_names;
+ int *calling_types;
+ size_t num_addrs;
+ uint16_t port;
+
+ struct tevent_req **requests;
+ size_t num_sent;
+ size_t num_received;
+
+ int fd;
+ uint16_t chosen_port;
+ size_t chosen_index;
+};
+
+static void smbsock_any_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state);
+static bool smbsock_any_connect_send_next(
+ struct tevent_req *req, struct smbsock_any_connect_state *state);
+static void smbsock_any_connect_trynext(struct tevent_req *subreq);
+static void smbsock_any_connect_connected(struct tevent_req *subreq);
+
+struct tevent_req *smbsock_any_connect_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const struct sockaddr_storage *addrs,
+ const char **called_names,
+ int *called_types,
+ const char **calling_names,
+ int *calling_types,
+ size_t num_addrs, uint16_t port)
+{
+ struct tevent_req *req, *subreq;
+ struct smbsock_any_connect_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smbsock_any_connect_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->addrs = addrs;
+ state->num_addrs = num_addrs;
+ state->called_names = called_names;
+ state->called_types = called_types;
+ state->calling_names = calling_names;
+ state->calling_types = calling_types;
+ state->port = port;
+ state->fd = -1;
+
+ tevent_req_set_cleanup_fn(req, smbsock_any_connect_cleanup);
+
+ if (num_addrs == 0) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->requests = talloc_zero_array(state, struct tevent_req *,
+ num_addrs);
+ if (tevent_req_nomem(state->requests, req)) {
+ return tevent_req_post(req, ev);
+ }
+ if (!smbsock_any_connect_send_next(req, state)) {
+ return tevent_req_post(req, ev);
+ }
+ if (state->num_sent >= state->num_addrs) {
+ return req;
+ }
+ subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 10000));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req);
+ return req;
+}
+
+static void smbsock_any_connect_cleanup(struct tevent_req *req,
+ enum tevent_req_state req_state)
+{
+ struct smbsock_any_connect_state *state = tevent_req_data(
+ req, struct smbsock_any_connect_state);
+
+ TALLOC_FREE(state->requests);
+
+ if (req_state == TEVENT_REQ_DONE) {
+ /*
+ * Keep the socket open for the caller.
+ */
+ return;
+ }
+
+ if (state->fd != -1) {
+ close(state->fd);
+ state->fd = -1;
+ }
+}
+
+static void smbsock_any_connect_trynext(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbsock_any_connect_state *state = tevent_req_data(
+ req, struct smbsock_any_connect_state);
+ bool ret;
+
+ ret = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ if (!smbsock_any_connect_send_next(req, state)) {
+ return;
+ }
+ if (state->num_sent >= state->num_addrs) {
+ return;
+ }
+ subreq = tevent_wakeup_send(state, state->ev,
+ tevent_timeval_set(0, 10000));
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req);
+}
+
+static bool smbsock_any_connect_send_next(
+ struct tevent_req *req, struct smbsock_any_connect_state *state)
+{
+ struct tevent_req *subreq;
+
+ if (state->num_sent >= state->num_addrs) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return false;
+ }
+ subreq = smbsock_connect_send(
+ state->requests, state->ev, &state->addrs[state->num_sent],
+ state->port,
+ (state->called_names != NULL)
+ ? state->called_names[state->num_sent] : NULL,
+ (state->called_types != NULL)
+ ? state->called_types[state->num_sent] : -1,
+ (state->calling_names != NULL)
+ ? state->calling_names[state->num_sent] : NULL,
+ (state->calling_types != NULL)
+ ? state->calling_types[state->num_sent] : -1);
+ if (tevent_req_nomem(subreq, req)) {
+ return false;
+ }
+ tevent_req_set_callback(subreq, smbsock_any_connect_connected, req);
+
+ state->requests[state->num_sent] = subreq;
+ state->num_sent += 1;
+
+ return true;
+}
+
+static void smbsock_any_connect_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smbsock_any_connect_state *state = tevent_req_data(
+ req, struct smbsock_any_connect_state);
+ NTSTATUS status;
+ int fd = 0;
+ uint16_t chosen_port = 0;
+ size_t i;
+ size_t chosen_index = 0;
+
+ for (i=0; i<state->num_sent; i++) {
+ if (state->requests[i] == subreq) {
+ chosen_index = i;
+ break;
+ }
+ }
+ if (i == state->num_sent) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ status = smbsock_connect_recv(subreq, &fd, &chosen_port);
+
+ TALLOC_FREE(subreq);
+ state->requests[chosen_index] = NULL;
+
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * tevent_req_done() will kill all the other requests
+ * via smbsock_any_connect_cleanup().
+ */
+ state->fd = fd;
+ state->chosen_port = chosen_port;
+ state->chosen_index = chosen_index;
+ tevent_req_done(req);
+ return;
+ }
+
+ state->num_received += 1;
+ if (state->num_received < state->num_addrs) {
+ /*
+ * More addrs pending, wait for the others
+ */
+ return;
+ }
+
+ /*
+ * This is the last response, none succeeded.
+ */
+ tevent_req_nterror(req, status);
+ return;
+}
+
+NTSTATUS smbsock_any_connect_recv(struct tevent_req *req, int *pfd,
+ size_t *chosen_index,
+ uint16_t *chosen_port)
+{
+ struct smbsock_any_connect_state *state = tevent_req_data(
+ req, struct smbsock_any_connect_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *pfd = state->fd;
+ state->fd = -1;
+ if (chosen_index != NULL) {
+ *chosen_index = state->chosen_index;
+ }
+ if (chosen_port != NULL) {
+ *chosen_port = state->chosen_port;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smbsock_any_connect(const struct sockaddr_storage *addrs,
+ const char **called_names,
+ int *called_types,
+ const char **calling_names,
+ int *calling_types,
+ size_t num_addrs,
+ uint16_t port,
+ int sec_timeout,
+ int *pfd, size_t *chosen_index,
+ uint16_t *chosen_port)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = smbsock_any_connect_send(frame, ev, addrs,
+ called_names, called_types,
+ calling_names, calling_types,
+ num_addrs, port);
+ if (req == NULL) {
+ goto fail;
+ }
+ if ((sec_timeout != 0) &&
+ !tevent_req_set_endtime(
+ req, ev, timeval_current_ofs(sec_timeout, 0))) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = smbsock_any_connect_recv(req, pfd, chosen_index, chosen_port);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c
new file mode 100644
index 0000000..c40eb79
--- /dev/null
+++ b/source3/libsmb/trusts_util.c
@@ -0,0 +1,630 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Routines to operate on various trust relationships
+ * Copyright (C) Andrew Bartlett 2001
+ * Copyright (C) Rafal Szczesniak 2003
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../libcli/auth/netlogon_creds_cli.h"
+#include "rpc_client/cli_netlogon.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/secrets.h"
+#include "secrets.h"
+#include "passdb.h"
+#include "libsmb/libsmb.h"
+#include "source3/include/messages.h"
+#include "source3/include/g_lock.h"
+#include "lib/util/util_tdb.h"
+
+/*********************************************************
+ Change the domain password on the PDC.
+ Do most of the legwork ourselves. Caller must have
+ already setup the connection to the NETLOGON pipe
+**********************************************************/
+
+struct trust_pw_change_state {
+ struct g_lock_ctx *g_ctx;
+ char *g_lock_key;
+};
+
+static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
+{
+ g_lock_unlock(state->g_ctx,
+ string_term_tdb_data(state->g_lock_key));
+ return 0;
+}
+
+char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
+ enum netr_SchannelType sec_channel_type,
+ int security)
+{
+ /*
+ * use secure defaults, which match
+ * what windows uses for computer passwords.
+ *
+ * We used to have min=128 and max=255 here, but
+ * it's a bad idea because of bugs in the Windows
+ * RODC/RWDC PasswordUpdateForward handling via
+ * NetrLogonSendToSam.
+ *
+ * See https://bugzilla.samba.org/show_bug.cgi?id=14984
+ */
+ size_t min = 120;
+ size_t max = 120;
+
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ if (security == SEC_DOMAIN) {
+ /*
+ * The maximum length of a trust account password.
+ * Used when we randomly create it, 15 char passwords
+ * exceed NT4's max password length.
+ */
+ min = 14;
+ max = 14;
+ }
+ break;
+ case SEC_CHAN_DNS_DOMAIN:
+ /*
+ * new_len * 2 = 498 bytes is the largest possible length
+ * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
+ * and a confounder with at least 2 bytes is required.
+ *
+ * Windows uses new_len = 120 => 240 bytes (utf16)
+ */
+ min = 120;
+ max = 120;
+ break;
+ case SEC_CHAN_DOMAIN:
+ /*
+ * The maximum length of a trust account password.
+ * Used when we randomly create it, 15 char passwords
+ * exceed NT4's max password length.
+ */
+ min = 14;
+ max = 14;
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Create a random machine account password
+ * We create a random buffer and convert that to utf8.
+ * This is similar to what windows is doing.
+ */
+ return generate_random_machine_password(mem_ctx, min, max);
+}
+
+/*
+ * Temporary function to wrap cli_auth in a lck
+ */
+
+static NTSTATUS netlogon_creds_cli_lck_auth(
+ struct netlogon_creds_cli_context *context,
+ struct dcerpc_binding_handle *b,
+ uint8_t num_nt_hashes,
+ const struct samr_Password * const *nt_hashes,
+ uint8_t *idx_nt_hashes)
+{
+ struct netlogon_creds_cli_lck *lck;
+ NTSTATUS status;
+
+ status = netlogon_creds_cli_lck(
+ context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
+ talloc_tos(), &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
+ idx_nt_hashes);
+ TALLOC_FREE(lck);
+
+ return status;
+}
+
+NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
+ struct messaging_context *msg_ctx,
+ struct dcerpc_binding_handle *b,
+ const char *domain,
+ const char *dcname,
+ bool force)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *context_name = NULL;
+ struct trust_pw_change_state *state;
+ struct cli_credentials *creds = NULL;
+ struct secrets_domain_info1 *info = NULL;
+ struct secrets_domain_info1_change *prev = NULL;
+ const struct samr_Password *current_nt_hash = NULL;
+ const struct samr_Password *previous_nt_hash = NULL;
+ uint8_t num_nt_hashes = 0;
+ uint8_t idx = 0;
+ const struct samr_Password *nt_hashes[1+3] = { NULL, };
+ uint8_t idx_nt_hashes = 0;
+ uint8_t idx_current = UINT8_MAX;
+ enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
+ time_t pass_last_set_time;
+ uint32_t old_version = 0;
+ struct pdb_trusted_domain *td = NULL;
+ struct timeval g_timeout = { 0, };
+ int timeout = 0;
+ struct timeval tv = { 0, };
+ char *new_trust_pw_str = NULL;
+ size_t len = 0;
+ DATA_BLOB new_trust_pw_blob = data_blob_null;
+ uint32_t new_version = 0;
+ uint32_t *new_trust_version = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ state = talloc_zero(frame, struct trust_pw_change_state);
+ if (state == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->g_ctx = g_lock_ctx_init(state, msg_ctx);
+ if (state->g_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->g_lock_key = talloc_asprintf(state,
+ "trust_password_change_%s",
+ domain);
+ if (state->g_lock_key == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ g_timeout = timeval_current_ofs(10, 0);
+ status = g_lock_lock(state->g_ctx,
+ string_term_tdb_data(state->g_lock_key),
+ G_LOCK_WRITE, g_timeout, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("could not get g_lock on [%s]!\n",
+ state->g_lock_key));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ talloc_set_destructor(state, trust_pw_change_state_destructor);
+
+ status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
+ domain, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+ if (current_nt_hash == NULL) {
+ DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+ previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
+
+ old_version = cli_credentials_get_kvno(creds);
+ pass_last_set_time = cli_credentials_get_password_last_changed_time(creds);
+ sec_channel_type = cli_credentials_get_secure_channel_type(creds);
+
+ new_version = old_version + 1;
+
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ break;
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ status = pdb_get_trusted_domain(frame, domain, &td);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
+ domain, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ new_trust_version = &new_version;
+ break;
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ timeout = lp_machine_password_timeout();
+ if (timeout == 0) {
+ if (!force) {
+ DEBUG(10,("machine password never expires\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+ }
+
+ tv.tv_sec = pass_last_set_time;
+ DEBUG(10, ("password last changed %s\n",
+ timeval_string(talloc_tos(), &tv, false)));
+ tv.tv_sec += timeout;
+ DEBUGADD(10, ("password valid until %s\n",
+ timeval_string(talloc_tos(), &tv, false)));
+
+ if (!force && !timeval_expired(&tv)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
+ if (context_name == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Create a random machine account password
+ * We create a random buffer and convert that to utf8.
+ * This is similar to what windows is doing.
+ */
+ new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
+ lp_security());
+ if (new_trust_pw_str == NULL) {
+ DEBUG(0, ("trust_pw_new_value() failed\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ len = strlen(new_trust_pw_str);
+ ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
+ new_trust_pw_str, len,
+ (void **)&new_trust_pw_blob.data,
+ &new_trust_pw_blob.length);
+ if (!ok) {
+ status = NT_STATUS_UNMAPPABLE_CHARACTER;
+ if (errno == ENOMEM) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+ DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
+ "failed for of %s - %s\n",
+ domain, nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+ talloc_keep_secret(new_trust_pw_blob.data);
+
+ switch (sec_channel_type) {
+
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ status = secrets_prepare_password_change(domain, dcname,
+ new_trust_pw_str,
+ frame, &info, &prev);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ TALLOC_FREE(new_trust_pw_str);
+
+ if (prev != NULL) {
+ /*
+ * We had a failure before we changed the password.
+ */
+ nt_hashes[idx++] = &prev->password->nt_hash;
+
+ DEBUG(0,("%s : %s(%s): A password change was already "
+ "started against '%s' at %s. Trying to "
+ "recover...\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain,
+ prev->password->change_server,
+ nt_time_string(talloc_tos(),
+ prev->password->change_time)));
+ DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
+ "against '%s' at %s.\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain,
+ nt_errstr(prev->local_status),
+ nt_errstr(prev->remote_status),
+ prev->change_server,
+ nt_time_string(talloc_tos(),
+ prev->change_time)));
+ }
+
+ idx_current = idx;
+ nt_hashes[idx++] = &info->password->nt_hash;
+ if (info->old_password != NULL) {
+ nt_hashes[idx++] = &info->old_password->nt_hash;
+ }
+ if (info->older_password != NULL) {
+ nt_hashes[idx++] = &info->older_password->nt_hash;
+ }
+
+ /*
+ * We use the password that's already persistent in
+ * our database in order to handle failures.
+ */
+ data_blob_free(&new_trust_pw_blob);
+ new_trust_pw_blob = info->next_change->password->cleartext_blob;
+ break;
+
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ idx_current = idx;
+ nt_hashes[idx++] = current_nt_hash;
+ if (previous_nt_hash != NULL) {
+ nt_hashes[idx++] = previous_nt_hash;
+ }
+ break;
+
+ default:
+ smb_panic("Unsupported secure channel type");
+ break;
+ }
+ num_nt_hashes = idx;
+
+ DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ /*
+ * Check which password the dc knows about.
+ *
+ * TODO:
+ * If the previous password is the only password in common with the dc,
+ * we better skip the password change, or use something like
+ * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
+ * local secrets before doing the change.
+ */
+ status = netlogon_creds_cli_lck_auth(context, b,
+ num_nt_hashes,
+ nt_hashes,
+ &idx_nt_hashes);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
+ context_name, num_nt_hashes, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ if (prev != NULL && idx_nt_hashes == 0) {
+ DEBUG(0,("%s : %s(%s): Verified new password remotely "
+ "without changing %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ status = secrets_finish_password_change(prev->password->change_server,
+ prev->password->change_time,
+ info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ if (idx_nt_hashes != idx_current) {
+ DEBUG(0,("%s : %s(%s): Verified older password remotely "
+ "skip changing %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ if (info == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ status = secrets_defer_password_change(dcname,
+ NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
+ NT_STATUS_NOT_COMMITTED,
+ info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ TALLOC_FREE(frame);
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ /*
+ * Return the result of trying to write the new password
+ * back into the trust account file.
+ */
+
+ switch (sec_channel_type) {
+
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ /*
+ * we called secrets_prepare_password_change() above.
+ */
+ break;
+
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ /*
+ * we need to get the sid first for the
+ * pdb_set_trusteddom_pw call
+ */
+ ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str,
+ &td->security_identifier);
+ if (!ok) {
+ DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ TALLOC_FREE(new_trust_pw_str);
+ break;
+
+ default:
+ smb_panic("Unsupported secure channel type");
+ break;
+ }
+
+ DEBUG(0,("%s : %s(%s): Changed password locally\n",
+ current_timestring(talloc_tos(), false), __func__, domain));
+
+ status = netlogon_creds_cli_ServerPasswordSet(context, b,
+ &new_trust_pw_blob,
+ new_trust_version);
+ if (!NT_STATUS_IS_OK(status)) {
+ NTSTATUS status2;
+ const char *fn = NULL;
+
+ ok = dcerpc_binding_handle_is_connected(b);
+
+ DEBUG(0,("%s : %s(%s) remote password change with %s failed "
+ "- %s (%s)\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name,
+ nt_errstr(status),
+ ok ? "connected": "disconnected"));
+
+ if (!ok) {
+ /*
+ * The connection is broken, we don't
+ * know if the password was changed,
+ * we hope to have more luck next time.
+ */
+ status2 = secrets_failed_password_change(dcname,
+ NT_STATUS_NOT_COMMITTED,
+ status,
+ info);
+ fn = "secrets_failed_password_change";
+ } else {
+ /*
+ * The server rejected the change, we don't
+ * retry and defer the change to the next
+ * "machine password timeout" interval.
+ */
+ status2 = secrets_defer_password_change(dcname,
+ NT_STATUS_NOT_COMMITTED,
+ status,
+ info);
+ fn = "secrets_defer_password_change";
+ }
+ if (!NT_STATUS_IS_OK(status2)) {
+ DEBUG(0, ("%s() failed for domain %s!\n",
+ fn, domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ switch (sec_channel_type) {
+
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ status = secrets_finish_password_change(
+ info->next_change->change_server,
+ info->next_change->change_time,
+ info);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ DEBUG(0,("%s : %s(%s): Finished password change.\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain));
+ break;
+
+ case SEC_CHAN_DNS_DOMAIN:
+ case SEC_CHAN_DOMAIN:
+ /*
+ * we used pdb_set_trusteddom_pw().
+ */
+ break;
+
+ default:
+ smb_panic("Unsupported secure channel type");
+ break;
+ }
+
+ ok = cli_credentials_set_utf16_password(creds,
+ &new_trust_pw_blob,
+ CRED_SPECIFIED);
+ if (!ok) {
+ DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+ if (current_nt_hash == NULL) {
+ DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+ }
+
+ /*
+ * Now we verify the new password.
+ */
+ idx = 0;
+ nt_hashes[idx++] = current_nt_hash;
+ num_nt_hashes = idx;
+ status = netlogon_creds_cli_lck_auth(context, b,
+ num_nt_hashes,
+ nt_hashes,
+ &idx_nt_hashes);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
+ context_name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c
new file mode 100644
index 0000000..b81d379
--- /dev/null
+++ b/source3/libsmb/unexpected.c
@@ -0,0 +1,751 @@
+/*
+ Unix SMB/CIFS implementation.
+ handle unexpected packets
+ Copyright (C) Andrew Tridgell 2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libsmb/unexpected.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "lib/util_tsock.h"
+#include "libsmb/nmblib.h"
+#include "lib/tsocket/tsocket.h"
+#include "lib/util/sys_rw.h"
+
+static const char *nmbd_socket_dir(void)
+{
+ return lp_parm_const_string(-1, "nmbd", "socket dir",
+ get_dyn_NMBDSOCKETDIR());
+}
+
+struct nb_packet_query {
+ enum packet_type type;
+ size_t mailslot_namelen;
+ int trn_id;
+};
+
+struct nb_packet_client;
+
+struct nb_packet_server {
+ struct tevent_context *ev;
+ int listen_sock;
+ struct tevent_fd *listen_fde;
+ int max_clients;
+ int num_clients;
+ struct nb_packet_client *clients;
+};
+
+struct nb_packet_client {
+ struct nb_packet_client *prev, *next;
+ struct nb_packet_server *server;
+
+ enum packet_type type;
+ int trn_id;
+ char *mailslot_name;
+
+ struct {
+ uint8_t byte;
+ struct iovec iov[1];
+ } ack;
+
+ struct tstream_context *sock;
+ struct tevent_queue *out_queue;
+};
+
+static int nb_packet_server_destructor(struct nb_packet_server *s);
+static void nb_packet_server_listener(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data);
+
+NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int max_clients,
+ struct nb_packet_server **presult)
+{
+ struct nb_packet_server *result;
+ NTSTATUS status;
+ int rc;
+
+ result = talloc_zero(mem_ctx, struct nb_packet_server);
+ if (result == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ result->ev = ev;
+ result->max_clients = max_clients;
+
+ result->listen_sock = create_pipe_sock(
+ nmbd_socket_dir(), "unexpected", 0755);
+ if (result->listen_sock == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ rc = listen(result->listen_sock, 5);
+ if (rc < 0) {
+ status = map_nt_error_from_unix(errno);
+ goto fail;
+ }
+ talloc_set_destructor(result, nb_packet_server_destructor);
+
+ result->listen_fde = tevent_add_fd(ev, result,
+ result->listen_sock,
+ TEVENT_FD_READ,
+ nb_packet_server_listener,
+ result);
+ if (result->listen_fde == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *presult = result;
+ return NT_STATUS_OK;
+fail:
+ TALLOC_FREE(result);
+ return status;
+}
+
+static int nb_packet_server_destructor(struct nb_packet_server *s)
+{
+ TALLOC_FREE(s->listen_fde);
+
+ if (s->listen_sock != -1) {
+ close(s->listen_sock);
+ s->listen_sock = -1;
+ }
+ return 0;
+}
+
+static int nb_packet_client_destructor(struct nb_packet_client *c);
+static ssize_t nb_packet_client_more(uint8_t *buf, size_t buflen,
+ void *private_data);
+static void nb_packet_got_query(struct tevent_req *req);
+static void nb_packet_client_ack_done(struct tevent_req *req);
+static void nb_packet_client_read_done(struct tevent_req *req);
+
+static void nb_packet_server_listener(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct nb_packet_server *server = talloc_get_type_abort(
+ private_data, struct nb_packet_server);
+ struct nb_packet_client *client;
+ struct tevent_req *req;
+ struct sockaddr_un sunaddr;
+ socklen_t len;
+ int sock;
+ int ret;
+
+ len = sizeof(sunaddr);
+
+ sock = accept(server->listen_sock, (struct sockaddr *)(void *)&sunaddr,
+ &len);
+ if (sock == -1) {
+ return;
+ }
+ smb_set_close_on_exec(sock);
+ DEBUG(6,("accepted socket %d\n", sock));
+
+ client = talloc_zero(server, struct nb_packet_client);
+ if (client == NULL) {
+ DEBUG(10, ("talloc failed\n"));
+ close(sock);
+ return;
+ }
+ ret = tstream_bsd_existing_socket(client, sock, &client->sock);
+ if (ret != 0) {
+ DEBUG(10, ("tstream_bsd_existing_socket failed\n"));
+ TALLOC_FREE(client);
+ close(sock);
+ return;
+ }
+ /* as server we want to fail early */
+ tstream_bsd_fail_readv_first_error(client->sock, true);
+
+ client->server = server;
+
+ client->out_queue = tevent_queue_create(
+ client, "unexpected packet output");
+ if (client->out_queue == NULL) {
+ DEBUG(10, ("tevent_queue_create failed\n"));
+ TALLOC_FREE(client);
+ return;
+ }
+
+ req = tstream_read_packet_send(client, ev, client->sock,
+ sizeof(struct nb_packet_query),
+ nb_packet_client_more, NULL);
+ if (req == NULL) {
+ DEBUG(10, ("tstream_read_packet_send failed\n"));
+ TALLOC_FREE(client);
+ return;
+ }
+ tevent_req_set_callback(req, nb_packet_got_query, client);
+
+ DLIST_ADD(server->clients, client);
+ server->num_clients += 1;
+
+ talloc_set_destructor(client, nb_packet_client_destructor);
+
+ if (server->num_clients > server->max_clients) {
+ DEBUG(10, ("Too many clients, dropping oldest\n"));
+
+ /*
+ * no TALLOC_FREE here, don't mess with the list structs
+ */
+ talloc_free(server->clients->prev);
+ }
+}
+
+static ssize_t nb_packet_client_more(uint8_t *buf, size_t buflen,
+ void *private_data)
+{
+ struct nb_packet_query q;
+ if (buflen > sizeof(struct nb_packet_query)) {
+ return 0;
+ }
+ /* Take care of alignment */
+ memcpy(&q, buf, sizeof(q));
+ if (q.mailslot_namelen > 1024) {
+ DEBUG(10, ("Got invalid mailslot namelen %d\n",
+ (int)q.mailslot_namelen));
+ return -1;
+ }
+ return q.mailslot_namelen;
+}
+
+static int nb_packet_client_destructor(struct nb_packet_client *c)
+{
+ tevent_queue_stop(c->out_queue);
+ TALLOC_FREE(c->sock);
+
+ DLIST_REMOVE(c->server->clients, c);
+ c->server->num_clients -= 1;
+ return 0;
+}
+
+static void nb_packet_got_query(struct tevent_req *req)
+{
+ struct nb_packet_client *client = tevent_req_callback_data(
+ req, struct nb_packet_client);
+ struct nb_packet_query q;
+ uint8_t *buf;
+ ssize_t nread;
+ int err;
+
+ nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err);
+ TALLOC_FREE(req);
+ if (nread < (ssize_t)sizeof(struct nb_packet_query)) {
+ DEBUG(10, ("read_packet_recv returned %d (%s)\n",
+ (int)nread,
+ (nread == -1) ? strerror(err) : "wrong length"));
+ TALLOC_FREE(client);
+ return;
+ }
+
+ /* Take care of alignment */
+ memcpy(&q, buf, sizeof(q));
+
+ if ((size_t)nread !=
+ sizeof(struct nb_packet_query) + q.mailslot_namelen) {
+ DEBUG(10, ("nb_packet_got_query: Invalid mailslot namelength\n"));
+ TALLOC_FREE(client);
+ return;
+ }
+
+ client->trn_id = q.trn_id;
+ client->type = q.type;
+ if (q.mailslot_namelen > 0) {
+ client->mailslot_name = talloc_strndup(
+ client, (char *)buf + sizeof(q),
+ q.mailslot_namelen);
+ if (client->mailslot_name == NULL) {
+ TALLOC_FREE(client);
+ return;
+ }
+ }
+
+ client->ack.byte = 0;
+ client->ack.iov[0].iov_base = &client->ack.byte;
+ client->ack.iov[0].iov_len = 1;
+ req = tstream_writev_queue_send(client, client->server->ev,
+ client->sock,
+ client->out_queue,
+ client->ack.iov, 1);
+ if (req == NULL) {
+ DEBUG(10, ("tstream_writev_queue_send failed\n"));
+ TALLOC_FREE(client);
+ return;
+ }
+ tevent_req_set_callback(req, nb_packet_client_ack_done, client);
+
+ req = tstream_read_packet_send(client, client->server->ev,
+ client->sock, 1, NULL, NULL);
+ if (req == NULL) {
+ DEBUG(10, ("Could not activate reader for client exit "
+ "detection\n"));
+ TALLOC_FREE(client);
+ return;
+ }
+ tevent_req_set_callback(req, nb_packet_client_read_done,
+ client);
+}
+
+static void nb_packet_client_ack_done(struct tevent_req *req)
+{
+ struct nb_packet_client *client = tevent_req_callback_data(
+ req, struct nb_packet_client);
+ ssize_t nwritten;
+ int err;
+
+ nwritten = tstream_writev_queue_recv(req, &err);
+
+ TALLOC_FREE(req);
+
+ if (nwritten == -1) {
+ DEBUG(10, ("tstream_writev_queue_recv failed: %s\n",
+ strerror(err)));
+ TALLOC_FREE(client);
+ return;
+ }
+}
+
+static void nb_packet_client_read_done(struct tevent_req *req)
+{
+ struct nb_packet_client *client = tevent_req_callback_data(
+ req, struct nb_packet_client);
+ ssize_t nread;
+ uint8_t *buf;
+ int err;
+
+ nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err);
+ TALLOC_FREE(req);
+ if (nread == 1) {
+ DEBUG(10, ("Protocol error, received data on write-only "
+ "unexpected socket: 0x%2.2x\n", (*buf)));
+ }
+ TALLOC_FREE(client);
+}
+
+static void nb_packet_client_send(struct nb_packet_client *client,
+ struct packet_struct *p);
+
+void nb_packet_dispatch(struct nb_packet_server *server,
+ struct packet_struct *p)
+{
+ struct nb_packet_client *c;
+ uint16_t trn_id;
+
+ switch (p->packet_type) {
+ case NMB_PACKET:
+ trn_id = p->packet.nmb.header.name_trn_id;
+ break;
+ case DGRAM_PACKET:
+ trn_id = p->packet.dgram.header.dgm_id;
+ break;
+ default:
+ DEBUG(10, ("Got invalid packet type %d\n",
+ (int)p->packet_type));
+ return;
+ }
+ for (c = server->clients; c != NULL; c = c->next) {
+
+ if (c->type != p->packet_type) {
+ DEBUG(10, ("client expects packet %d, got %d\n",
+ c->type, p->packet_type));
+ continue;
+ }
+
+ if (p->packet_type == NMB_PACKET) {
+ /*
+ * See if the client specified transaction
+ * ID. Filter if it did.
+ */
+ if ((c->trn_id != -1) &&
+ (c->trn_id != trn_id)) {
+ DEBUG(10, ("client expects trn %d, got %d\n",
+ c->trn_id, trn_id));
+ continue;
+ }
+ } else {
+ /*
+ * See if the client specified a mailslot
+ * name. Filter if it did.
+ */
+ if ((c->mailslot_name != NULL) &&
+ !match_mailslot_name(p, c->mailslot_name)) {
+ continue;
+ }
+ }
+ nb_packet_client_send(c, p);
+ }
+}
+
+struct nb_packet_client_header {
+ size_t len;
+ enum packet_type type;
+ time_t timestamp;
+ struct in_addr ip;
+ int port;
+};
+
+struct nb_packet_client_state {
+ struct nb_packet_client *client;
+ struct iovec iov[2];
+ struct nb_packet_client_header hdr;
+ char buf[1024];
+};
+
+static void nb_packet_client_send_done(struct tevent_req *req);
+
+static void nb_packet_client_send(struct nb_packet_client *client,
+ struct packet_struct *p)
+{
+ struct nb_packet_client_state *state;
+ struct tevent_req *req;
+
+ if (tevent_queue_length(client->out_queue) > 10) {
+ /*
+ * Skip clients that don't listen anyway, some form of DoS
+ * protection
+ */
+ return;
+ }
+
+ state = talloc_zero(client, struct nb_packet_client_state);
+ if (state == NULL) {
+ DEBUG(10, ("talloc failed\n"));
+ return;
+ }
+
+ state->client = client;
+
+ state->hdr.ip = p->ip;
+ state->hdr.port = p->port;
+ state->hdr.timestamp = p->timestamp;
+ state->hdr.type = p->packet_type;
+ state->hdr.len = build_packet(state->buf, sizeof(state->buf), p);
+
+ state->iov[0].iov_base = (char *)&state->hdr;
+ state->iov[0].iov_len = sizeof(state->hdr);
+ state->iov[1].iov_base = state->buf;
+ state->iov[1].iov_len = state->hdr.len;
+
+ req = tstream_writev_queue_send(state, client->server->ev,
+ client->sock,
+ client->out_queue,
+ state->iov, 2);
+ if (req == NULL) {
+ DEBUG(10, ("tstream_writev_queue_send failed\n"));
+ return;
+ }
+ tevent_req_set_callback(req, nb_packet_client_send_done, state);
+}
+
+static void nb_packet_client_send_done(struct tevent_req *req)
+{
+ struct nb_packet_client_state *state = tevent_req_callback_data(
+ req, struct nb_packet_client_state);
+ struct nb_packet_client *client = state->client;
+ ssize_t nwritten;
+ int err;
+
+ nwritten = tstream_writev_queue_recv(req, &err);
+
+ TALLOC_FREE(req);
+ TALLOC_FREE(state);
+
+ if (nwritten == -1) {
+ DEBUG(10, ("tstream_writev_queue failed: %s\n", strerror(err)));
+ TALLOC_FREE(client);
+ return;
+ }
+}
+
+struct nb_packet_reader {
+ struct tstream_context *sock;
+};
+
+struct nb_packet_reader_state {
+ struct tevent_context *ev;
+ struct nb_packet_query query;
+ const char *mailslot_name;
+ struct iovec iov[2];
+ struct nb_packet_reader *reader;
+};
+
+static void nb_packet_reader_connected(struct tevent_req *subreq);
+static void nb_packet_reader_sent_query(struct tevent_req *subreq);
+static void nb_packet_reader_got_ack(struct tevent_req *subreq);
+
+struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum packet_type type,
+ int trn_id,
+ const char *mailslot_name)
+{
+ struct tevent_req *req, *subreq;
+ struct nb_packet_reader_state *state;
+ struct tsocket_address *laddr;
+ char *rpath;
+ struct tsocket_address *raddr;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct nb_packet_reader_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->query.trn_id = trn_id;
+ state->query.type = type;
+ state->mailslot_name = mailslot_name;
+
+ if (mailslot_name != NULL) {
+ state->query.mailslot_namelen = strlen(mailslot_name);
+ }
+
+ state->reader = talloc_zero(state, struct nb_packet_reader);
+ if (tevent_req_nomem(state->reader, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ret = tsocket_address_unix_from_path(state, NULL, &laddr);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+ rpath = talloc_asprintf(state, "%s/%s", nmbd_socket_dir(),
+ "unexpected");
+ if (tevent_req_nomem(rpath, req)) {
+ return tevent_req_post(req, ev);
+ }
+ ret = tsocket_address_unix_from_path(state, rpath, &raddr);
+ if (ret != 0) {
+ tevent_req_nterror(req, map_nt_error_from_unix(errno));
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = tstream_unix_connect_send(state, ev, laddr, raddr);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nb_packet_reader_connected, req);
+ return req;
+}
+
+static void nb_packet_reader_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_packet_reader_state *state = tevent_req_data(
+ req, struct nb_packet_reader_state);
+ int res, err;
+ int num_iovecs = 1;
+
+ res = tstream_unix_connect_recv(subreq, &err, state->reader,
+ &state->reader->sock);
+ TALLOC_FREE(subreq);
+ if (res == -1) {
+ DEBUG(10, ("tstream_unix_connect failed: %s\n", strerror(err)));
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+
+ state->iov[0].iov_base = (char *)&state->query;
+ state->iov[0].iov_len = sizeof(state->query);
+
+ if (state->mailslot_name != NULL) {
+ num_iovecs = 2;
+ state->iov[1].iov_base = discard_const_p(
+ char, state->mailslot_name);
+ state->iov[1].iov_len = state->query.mailslot_namelen;
+ }
+
+ subreq = tstream_writev_send(state, state->ev, state->reader->sock,
+ state->iov, num_iovecs);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_packet_reader_sent_query, req);
+}
+
+static void nb_packet_reader_sent_query(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_packet_reader_state *state = tevent_req_data(
+ req, struct nb_packet_reader_state);
+ ssize_t written;
+ int err;
+
+ written = tstream_writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (written == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ if ((size_t)written !=
+ sizeof(state->query) + state->query.mailslot_namelen) {
+ tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
+ return;
+ }
+ subreq = tstream_read_packet_send(state, state->ev,
+ state->reader->sock,
+ 1, NULL, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, nb_packet_reader_got_ack, req);
+}
+
+static void nb_packet_reader_got_ack(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_packet_reader_state *state = tevent_req_data(
+ req, struct nb_packet_reader_state);
+ ssize_t nread;
+ int err;
+ uint8_t *buf;
+
+ nread = tstream_read_packet_recv(subreq, state, &buf, &err);
+ TALLOC_FREE(subreq);
+ if (nread == -1) {
+ DEBUG(10, ("read_packet_recv returned %s\n",
+ strerror(err)));
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ if (nread != 1) {
+ DBG_DEBUG("read = %zd, expected 1\n", nread);
+ tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS nb_packet_reader_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct nb_packet_reader **preader)
+{
+ struct nb_packet_reader_state *state = tevent_req_data(
+ req, struct nb_packet_reader_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ *preader = talloc_move(mem_ctx, &state->reader);
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+struct nb_packet_read_state {
+ struct nb_packet_client_header hdr;
+ uint8_t *buf;
+ size_t buflen;
+};
+
+static ssize_t nb_packet_read_more(uint8_t *buf, size_t buflen, void *p);
+static void nb_packet_read_done(struct tevent_req *subreq);
+
+struct tevent_req *nb_packet_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct nb_packet_reader *reader)
+{
+ struct tevent_req *req, *subreq;
+ struct nb_packet_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct nb_packet_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ subreq = tstream_read_packet_send(state, ev, reader->sock,
+ sizeof(struct nb_packet_client_header),
+ nb_packet_read_more, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, nb_packet_read_done, req);
+ return req;
+}
+
+static ssize_t nb_packet_read_more(uint8_t *buf, size_t buflen, void *p)
+{
+ struct nb_packet_read_state *state = talloc_get_type_abort(
+ p, struct nb_packet_read_state);
+
+ if (buflen > sizeof(struct nb_packet_client_header)) {
+ /*
+ * Been here, done
+ */
+ return 0;
+ }
+ memcpy(&state->hdr, buf, sizeof(struct nb_packet_client_header));
+ return state->hdr.len;
+}
+
+static void nb_packet_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct nb_packet_read_state *state = tevent_req_data(
+ req, struct nb_packet_read_state);
+ ssize_t nread;
+ int err;
+
+ nread = tstream_read_packet_recv(subreq, state, &state->buf, &err);
+ if (nread == -1) {
+ tevent_req_nterror(req, map_nt_error_from_unix(err));
+ return;
+ }
+ state->buflen = nread;
+ tevent_req_done(req);
+}
+
+NTSTATUS nb_packet_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct packet_struct **ppacket)
+{
+ struct nb_packet_read_state *state = tevent_req_data(
+ req, struct nb_packet_read_state);
+ struct nb_packet_client_header hdr;
+ struct packet_struct *packet;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ memcpy(&hdr, state->buf, sizeof(hdr));
+
+ packet = parse_packet_talloc(
+ mem_ctx,
+ (char *)state->buf + sizeof(struct nb_packet_client_header),
+ state->buflen - sizeof(struct nb_packet_client_header),
+ state->hdr.type, state->hdr.ip, state->hdr.port);
+ if (packet == NULL) {
+ tevent_req_received(req);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *ppacket = packet;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/unexpected.h b/source3/libsmb/unexpected.h
new file mode 100644
index 0000000..270976b
--- /dev/null
+++ b/source3/libsmb/unexpected.h
@@ -0,0 +1,49 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBSMB_UNEXPECTED_H__
+#define __LIBSMB_UNEXPECTED_H__
+
+#include "replace.h"
+#include <tevent.h>
+#include "libcli/util/ntstatus.h"
+#include "nameserv.h"
+
+struct nb_packet_server;
+struct nb_packet_reader;
+
+NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int max_clients,
+ struct nb_packet_server **presult);
+void nb_packet_dispatch(struct nb_packet_server *server,
+ struct packet_struct *p);
+struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ enum packet_type type,
+ int trn_id,
+ const char *mailslot_name);
+NTSTATUS nb_packet_reader_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct nb_packet_reader **preader);
+struct tevent_req *nb_packet_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct nb_packet_reader *reader);
+NTSTATUS nb_packet_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct packet_struct **ppacket);
+
+#endif
diff --git a/source3/libsmb/wscript b/source3/libsmb/wscript
new file mode 100644
index 0000000..8ffdec6
--- /dev/null
+++ b/source3/libsmb/wscript
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+def build(bld):
+ bld.SAMBA3_LIBRARY('smbclient',
+ source='''
+ libsmb_cache.c
+ libsmb_compat.c
+ libsmb_context.c
+ libsmb_dir.c
+ libsmb_file.c
+ libsmb_misc.c
+ libsmb_path.c
+ libsmb_printjob.c
+ libsmb_server.c
+ libsmb_stat.c
+ libsmb_xattr.c
+ libsmb_setget.c''',
+ public_deps='''
+ pthread
+ talloc
+ smbconf
+ libsmb
+ KRBCLIENT
+ msrpc3
+ libcli_lsa3''',
+ public_headers='../include/libsmbclient.h',
+ abi_directory='ABI',
+ abi_match='smbc_*',
+ vnum='0.8.0',
+ pc_files='smbclient.pc')