summaryrefslogtreecommitdiffstats
path: root/source3/libsmb/async_smb.c
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/async_smb.c
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/async_smb.c')
-rw-r--r--source3/libsmb/async_smb.c269
1 files changed, 269 insertions, 0 deletions
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;
+}