diff options
Diffstat (limited to 'source3/libsmb/async_smb.c')
-rw-r--r-- | source3/libsmb/async_smb.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c new file mode 100644 index 0000000..bcb24a4 --- /dev/null +++ b/source3/libsmb/async_smb.c @@ -0,0 +1,264 @@ +/* + 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; + } + + 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; +} |