s3: Explicitly handle inbuf in cli_message_start_done
[samba.git] / source3 / libsmb / climessage.c
index 2a195753ae574c803f58eb6fb15c537079fd0f95..bdf2977af9c4cff744d397b402dec5bcbe7ca7fb 100644 (file)
-/* 
+/*
    Unix SMB/CIFS implementation.
    client message handling routines
    Copyright (C) Andrew Tridgell 1994-1998
-   
+
    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"
 
+struct cli_message_start_state {
+       uint16_t grp;
+};
+
+static void cli_message_start_done(struct tevent_req *subreq);
 
-/****************************************************************************
-start a message sequence
-****************************************************************************/
-int cli_message_start_build(struct cli_state *cli, char *host, char *username)
+static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
+                                                struct tevent_context *ev,
+                                                struct cli_state *cli,
+                                                const char *host,
+                                                const char *username)
 {
-       char *p;
-
-       /* construct a SMBsendstrt command */
-       memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,0,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendstrt);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
-       cli_setup_packet(cli);
-       
-       p = smb_buf(cli->outbuf);
+       struct tevent_req *req, *subreq;
+       struct cli_message_start_state *state;
+       char *htmp = NULL;
+       char *utmp = NULL;
+       size_t hlen, ulen;
+       uint8_t *bytes, *p;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_start_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+                                  username, strlen(username)+1,
+                                  &utmp, &ulen, true)) {
+               goto fail;
+       }
+       if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+                                  host, strlen(host)+1,
+                                  &htmp, &hlen, true)) {
+               goto fail;
+       }
+
+       bytes = talloc_array(state, uint8_t, ulen+hlen+2);
+       if (bytes == NULL) {
+               goto fail;
+       }
+       p = bytes;
+
        *p++ = 4;
-       p += clistr_push(cli, p, username, -1, STR_ASCII|STR_TERMINATE);
+       memcpy(p, utmp, ulen);
        *p++ = 4;
-       p += clistr_push(cli, p, host, -1, STR_ASCII|STR_TERMINATE);
-       
-       cli_setup_bcc(cli, p);
+       memcpy(p, htmp, hlen);
+       TALLOC_FREE(htmp);
+       TALLOC_FREE(utmp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL,
+                             talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_start_done, req);
+       return req;
+fail:
+       TALLOC_FREE(htmp);
+       TALLOC_FREE(utmp);
+       tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+       return tevent_req_post(req, ev);
+}
+
+static void cli_message_start_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_start_state *state = tevent_req_data(
+               req, struct cli_message_start_state);
+       NTSTATUS status;
+       uint8_t wct;
+       uint16_t *vwv;
+       uint8_t *inbuf;
+
+       status = cli_smb_recv(subreq, state, &inbuf, 0, &wct, &vwv,
+                             NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(subreq);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       if (wct >= 1) {
+               state->grp = SVAL(vwv+0, 0);
+       } else {
+               state->grp = 0;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_message_start_recv(struct tevent_req *req,
+                                      uint16_t *pgrp)
+{
+       struct cli_message_start_state *state = tevent_req_data(
+               req, struct cli_message_start_state);
+       NTSTATUS status;
 
-       return(PTR_DIFF(p, cli->outbuf));
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *pgrp = state->grp;
+       return NT_STATUS_OK;
 }
 
-bool cli_message_start(struct cli_state *cli, char *host, char *username,
-                             int *grp)
+struct cli_message_text_state {
+       uint16_t vwv;
+};
+
+static void cli_message_text_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               uint16_t grp,
+                                               const char *msg,
+                                               int msglen)
 {
-       cli_message_start_build(cli, host, username);
-       
-       cli_send_smb(cli);      
-       
-       if (!cli_receive_smb(cli)) {
-               return False;
+       struct tevent_req *req, *subreq;
+       struct cli_message_text_state *state;
+       char *tmp;
+       size_t tmplen;
+       uint8_t *bytes;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_text_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       if (cli_is_error(cli)) return False;
+       SSVAL(&state->vwv, 0, grp);
 
-       *grp = SVAL(cli->inbuf,smb_vwv0);
+       if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
+                                 &tmp, &tmplen, true)) {
+               msg = tmp;
+               msglen = tmplen;
+       } else {
+               DEBUG(3, ("Conversion failed, sending message in UNIX "
+                         "charset\n"));
+               tmp = NULL;
+       }
 
-       return True;
+       bytes = talloc_array(state, uint8_t, msglen+3);
+       if (tevent_req_nomem(bytes, req)) {
+               TALLOC_FREE(tmp);
+               return tevent_req_post(req, ev);
+       }
+       SCVAL(bytes, 0, 0);     /* pad */
+       SSVAL(bytes, 1, msglen);
+       memcpy(bytes+3, msg, msglen);
+       TALLOC_FREE(tmp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 1, &state->vwv,
+                             talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_text_done, req);
+       return req;
 }
 
+static void cli_message_text_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
 
-/****************************************************************************
-send a message 
-****************************************************************************/
-int cli_message_text_build(struct cli_state *cli, char *msg, int len, int grp)
+static NTSTATUS cli_message_text_recv(struct tevent_req *req)
 {
-       char *msgdos;
-       int lendos;
-       char *p;
-
-       memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,1,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendtxt);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
-       cli_setup_packet(cli);
-
-       SSVAL(cli->outbuf,smb_vwv0,grp);
-       
-       p = smb_buf(cli->outbuf);
-       *p++ = 1;
-
-       if ((lendos = (int)convert_string_allocate(NULL,CH_UNIX, CH_DOS, msg,len, (void **)(void *)&msgdos, True)) < 0 || !msgdos) {
-               DEBUG(3,("Conversion failed, sending message in UNIX charset\n"));
-               SSVAL(p, 0, len); p += 2;
-               memcpy(p, msg, len);
-               p += len;
-       } else {
-               SSVAL(p, 0, lendos); p += 2;
-               memcpy(p, msgdos, lendos);
-               p += lendos;
-               SAFE_FREE(msgdos);
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_message_end_state {
+       uint16_t vwv;
+};
+
+static void cli_message_end_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               uint16_t grp)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_message_end_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_end_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       SSVAL(&state->vwv, 0, grp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 1, &state->vwv,
+                             0, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
        }
+       tevent_req_set_callback(subreq, cli_message_end_done, req);
+       return req;
+}
 
-       cli_setup_bcc(cli, p);
+static void cli_message_end_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
 
-       return(PTR_DIFF(p, cli->outbuf));
+static NTSTATUS cli_message_end_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
-bool cli_message_text(struct cli_state *cli, char *msg, int len, int grp)
+struct cli_message_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       size_t sent;
+       const char *message;
+       uint16_t grp;
+};
+
+static void cli_message_started(struct tevent_req *subreq);
+static void cli_message_sent(struct tevent_req *subreq);
+static void cli_message_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct cli_state *cli,
+                                   const char *host, const char *username,
+                                   const char *message)
 {
-       cli_message_text_build(cli, msg, len, grp);
+       struct tevent_req *req, *subreq;
+       struct cli_message_state *state;
 
-       cli_send_smb(cli);
+       req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->sent = 0;
+       state->message = message;
+
+       subreq = cli_message_start_send(state, ev, cli, host, username);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_started, req);
+       return req;
+}
 
-       if (!cli_receive_smb(cli)) {
-               return False;
+static void cli_message_started(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_state *state = tevent_req_data(
+               req, struct cli_message_state);
+       NTSTATUS status;
+       size_t thistime;
+
+       status = cli_message_start_recv(subreq, &state->grp);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
        }
 
-       if (cli_is_error(cli)) return False;
+       thistime = MIN(127, strlen(state->message));
 
-       return True;
-}      
+       subreq = cli_message_text_send(state, state->ev, state->cli,
+                                      state->grp, state->message, thistime);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       state->sent += thistime;
+       tevent_req_set_callback(subreq, cli_message_sent, req);
+}
 
-/****************************************************************************
-end a message 
-****************************************************************************/
-int cli_message_end_build(struct cli_state *cli, int grp)
+static void cli_message_sent(struct tevent_req *subreq)
 {
-       char *p;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_state *state = tevent_req_data(
+               req, struct cli_message_state);
+       NTSTATUS status;
+       size_t left, thistime;
+
+       status = cli_message_text_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
 
-       memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,1,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendend);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
+       if (state->sent >= strlen(state->message)) {
+               subreq = cli_message_end_send(state, state->ev, state->cli,
+                                             state->grp);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, cli_message_done, req);
+               return;
+       }
 
-       SSVAL(cli->outbuf,smb_vwv0,grp);
+       left = strlen(state->message) - state->sent;
+       thistime = MIN(127, left);
 
-       cli_setup_packet(cli);
+       subreq = cli_message_text_send(state, state->ev, state->cli,
+                                      state->grp,
+                                      state->message + state->sent,
+                                      thistime);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       state->sent += thistime;
+       tevent_req_set_callback(subreq, cli_message_sent, req);
+}
 
-       p = smb_buf(cli->outbuf);
+static void cli_message_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_message_end_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
 
-       return(PTR_DIFF(p, cli->outbuf));
+NTSTATUS cli_message_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
-bool cli_message_end(struct cli_state *cli, int grp)
+NTSTATUS cli_message(struct cli_state *cli, const char *host,
+                    const char *username, const char *message)
 {
-       cli_message_end_build(cli, grp);
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_OK;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
 
-       cli_send_smb(cli);
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
 
-       if (!cli_receive_smb(cli)) {
-               return False;
+       req = cli_message_send(frame, ev, cli, host, username, message);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
 
-       if (cli_is_error(cli)) return False;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
 
-       return True;
-}      
+       status = cli_message_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}