From 8b4a3681e511adafe0efea3d64bad5135867477c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 9 Jul 2011 10:12:11 +0200 Subject: s3:smb2cli_base: make use of tevent_req_defer_callback() In order to notify requests of transport layer errors, we need to defer the triggering of the callbacks, otherwise we may crash, if one of the callbacks destroys the cli_state. metze --- source3/libsmb/smb2cli_base.c | 92 ++++++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 28 deletions(-) (limited to 'source3/libsmb/smb2cli_base.c') 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; iev); + /* * Note: here we use talloc_reference() in a way * that does not expose it to the caller. @@ -505,20 +555,6 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq) tevent_req_done(req); } - 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); } -- cgit