diff options
-rw-r--r-- | source3/libsmb/smb2cli_base.c | 92 |
1 files changed, 64 insertions, 28 deletions
diff --git a/source3/libsmb/smb2cli_base.c b/source3/libsmb/smb2cli_base.c index 1b5a8a6798..ac7d423b57 100644 --- a/source3/libsmb/smb2cli_base.c +++ b/source3/libsmb/smb2cli_base.c @@ -139,6 +139,36 @@ static bool smb2cli_req_set_pending(struct tevent_req *req) return true; } +static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status) +{ + if (cli->fd != -1) { + close(cli->fd); + cli->fd = -1; + } + + /* + * Cancel all pending requests. We don't do a for-loop walking + * cli->pending because that array changes in + * cli_smb_req_destructor(). + */ + while (talloc_array_length(cli->pending) > 0) { + struct tevent_req *req; + struct smb2cli_req_state *state; + + req = cli->pending[0]; + state = tevent_req_data(req, struct smb2cli_req_state); + + smb2cli_req_unset_pending(req); + + /* + * we need to defer the callback, because we may notify more + * then one caller. + */ + tevent_req_defer_callback(req, state->ev); + tevent_req_nterror(req, status); + } +} + struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, @@ -312,11 +342,8 @@ static void smb2cli_writev_done(struct tevent_req *subreq) nwritten = writev_recv(subreq, &err); TALLOC_FREE(subreq); if (nwritten == -1) { - if (state->cli->fd != -1) { - close(state->cli->fd); - state->cli->fd = -1; - } - tevent_req_nterror(req, map_nt_error_from_unix(err)); + /* here, we need to notify all pending requests */ + smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err)); return; } } @@ -457,18 +484,28 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq) received = read_smb_recv(subreq, frame, &inbuf, &err); TALLOC_FREE(subreq); if (received == -1) { - if (cli->fd != -1) { - close(cli->fd); - cli->fd = -1; - } - status = map_nt_error_from_unix(err); - goto fail; + /* + * We need to close the connection and notify + * all pending requests. + */ + smb2cli_notify_pending(cli, map_nt_error_from_unix(err)); + TALLOC_FREE(frame); + return; } status = smb2cli_inbuf_parse_compound(inbuf, frame, &iov, &num_iov); if (!NT_STATUS_IS_OK(status)) { - goto fail; + /* + * if we cannot parse the incoming pdu, + * the connection becomes unusable. + * + * We need to close the connection and notify + * all pending requests. + */ + smb2cli_notify_pending(cli, status); + TALLOC_FREE(frame); + return; } for (i=1; i<num_iov; i+=3) { @@ -481,14 +518,27 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq) cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID)); if (req == NULL) { /* - * oplock breaks ?? + * TODO: handle oplock breaks and async responses + */ + + /* + * We need to close the connection and notify + * all pending requests. */ - goto fail; + smb2cli_notify_pending(cli, status); + TALLOC_FREE(frame); + return; } smb2cli_req_unset_pending(req); state = tevent_req_data(req, struct smb2cli_req_state); /* + * There might be more than one response + * we need to defer the notifications + */ + tevent_req_defer_callback(req, state->ev); + + /* * Note: here we use talloc_reference() in a way * that does not expose it to the caller. */ @@ -506,20 +556,6 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq) } TALLOC_FREE(frame); - return; - fail: - /* - * Cancel all pending requests. We don't do a for-loop walking - * cli->pending because that array changes in - * cli_smb_req_destructor(). - */ - while (talloc_array_length(cli->pending) > 0) { - req = cli->pending[0]; - smb2cli_req_unset_pending(req); - tevent_req_nterror(req, status); - } - - TALLOC_FREE(frame); } NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, |