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) { 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, 0x03); /* 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 *)¶m[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; } clistr_pull_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; } }
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) { unsigned int data_len = 0; unsigned int param_len = 0; uint16_t setup[1]; 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 num_referrals; struct client_dfs_referral *referrals = NULL; NTSTATUS status; TALLOC_CTX *frame = talloc_stackframe(); *num_refs = 0; *refs = NULL; SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL); param = talloc_array(talloc_tos(), uint8_t, 2); if (!param) { status = NT_STATUS_NO_MEMORY; goto out; } SSVAL(param, 0, 0x03); /* max referral level */ param = trans2_bytes_push_str(param, cli_ucs2(cli), 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 *)¶m[2]; 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 ref_version; uint16 ref_size; int i; uint16 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; } clistr_pull_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; } }
/**************************************************************************** check for existance of a nttrans call ****************************************************************************/ static bool scan_nttrans(struct cli_state *cli, int op, int level, int fnum, int dnum, const char *fname) { uint32_t data_len = 0; uint32_t param_len = 0; uint32_t rparam_len, rdata_len; uint8_t *param = NULL; uint8_t data[DATA_SIZE]; NTSTATUS status; const char *newfname; const char *dname; memset(data, 0, sizeof(data)); data_len = 4; /* try with a info level only */ TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 2); if (param == NULL) return True; SSVAL(param, 0, level); param_len = talloc_get_size(param); status = try_nttrans_len(cli, "void", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); if (NT_STATUS_IS_OK(status)) return True; /* try with a file descriptor */ TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 6); if (param == NULL) return True; SSVAL(param, 0, fnum); SSVAL(param, 2, level); SSVAL(param, 4, 0); param_len = talloc_get_size(param); status = try_nttrans_len(cli, "fnum", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); if (NT_STATUS_IS_OK(status)) return True; /* try with a notify style */ TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 6); if (param == NULL) return True; SSVAL(param, 0, dnum); SSVAL(param, 2, dnum); SSVAL(param, 4, level); param_len = talloc_get_size(param); status = try_nttrans_len(cli, "notify", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); if (NT_STATUS_IS_OK(status)) return True; /* try with a file name */ TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 6); if (param == NULL) return True; SSVAL(param, 0, level); SSVAL(param, 2, 0); SSVAL(param, 4, 0); param = trans2_bytes_push_str(param, cli_ucs2(cli), fname, strlen(fname)+1, NULL); if (param == NULL) return True; param_len = talloc_get_size(param); status = try_nttrans_len(cli, "fname", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); if (NT_STATUS_IS_OK(status)) return True; /* try with a new file name */ newfname = "\\newfile.dat"; TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 6); if (param == NULL) return True; SSVAL(param, 0, level); SSVAL(param, 2, 0); SSVAL(param, 4, 0); param = trans2_bytes_push_str(param, cli_ucs2(cli), newfname, strlen(newfname)+1, NULL); if (param == NULL) return True; param_len = talloc_get_size(param); status = try_nttrans_len(cli, "newfile", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); cli_unlink(cli, newfname, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); cli_rmdir(cli, newfname); if (NT_STATUS_IS_OK(status)) return True; /* try dfs style */ dname = "\\testdir"; cli_mkdir(cli, dname); TALLOC_FREE(param); param = talloc_array(talloc_tos(), uint8_t, 2); if (param == NULL) return True; SSVAL(param, 0, level); param = trans2_bytes_push_str(param, cli_ucs2(cli), dname, strlen(dname)+1, NULL); if (param == NULL) return True; param_len = talloc_get_size(param); status = try_nttrans_len(cli, "dfs", op, level, param, data, param_len, &data_len, &rparam_len, &rdata_len); cli_rmdir(cli, dname); if (NT_STATUS_IS_OK(status)) return True; return False; }