v3-4-test: Pull in read_smb_send from master
[obnox/samba-ctdb.git] / source3 / libsmb / async_smb.c
index 52bfc7500926b654355b749485f8019c9bd3fcd8..8db24d202bb4ef1921c85d76b0f853c46ee15813 100644 (file)
 static void cli_state_handler(struct event_context *event_ctx,
                              struct fd_event *event, uint16 flags, void *p);
 
+/*
+ * Read an smb packet asynchronously, discard keepalives
+ */
+
+struct read_smb_state {
+       struct tevent_context *ev;
+       int fd;
+       uint8_t *buf;
+};
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data);
+static void read_smb_done(struct tevent_req *subreq);
+
+static struct tevent_req *read_smb_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       int fd)
+{
+       struct tevent_req *result, *subreq;
+       struct read_smb_state *state;
+
+       result = tevent_req_create(mem_ctx, &state, struct read_smb_state);
+       if (result == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->fd = fd;
+
+       subreq = read_packet_send(state, ev, fd, 4, read_smb_more, NULL);
+       if (subreq == NULL) {
+               goto fail;
+       }
+       tevent_req_set_callback(subreq, read_smb_done, result);
+       return result;
+ fail:
+       TALLOC_FREE(result);
+       return NULL;
+}
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data)
+{
+       if (buflen > 4) {
+               return 0;       /* We've been here, we're done */
+       }
+       return smb_len_large(buf);
+}
+
+static void read_smb_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct read_smb_state *state = tevent_req_data(
+               req, struct read_smb_state);
+       ssize_t len;
+       int err;
+
+       len = read_packet_recv(subreq, state, &state->buf, &err);
+       TALLOC_FREE(subreq);
+       if (len == -1) {
+               tevent_req_error(req, err);
+               return;
+       }
+
+       if (CVAL(state->buf, 0) == SMBkeepalive) {
+               subreq = read_packet_send(state, state->ev, state->fd, 4,
+                                         read_smb_more, NULL);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, read_smb_done, req);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static ssize_t read_smb_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                            uint8_t **pbuf, int *perrno)
+{
+       struct read_smb_state *state = tevent_req_data(
+               req, struct read_smb_state);
+
+       if (tevent_req_is_unix_error(req, perrno)) {
+               return -1;
+       }
+       *pbuf = talloc_move(mem_ctx, &state->buf);
+       return talloc_get_size(*pbuf);
+}
+
 /**
  * Fetch an error out of a NBT packet
  * @param[in] buf      The SMB packet
@@ -151,32 +238,6 @@ bool cli_in_chain(struct cli_state *cli)
        return (cli->chain_accumulator->num_async != 0);
 }
 
-/**
- * Is the SMB command able to hold an AND_X successor
- * @param[in] cmd      The SMB command in question
- * @retval Can we add a chained request after "cmd"?
- */
-
-static bool is_andx_req(uint8_t cmd)
-{
-       switch (cmd) {
-       case SMBtconX:
-       case SMBlockingX:
-       case SMBopenX:
-       case SMBreadX:
-       case SMBwriteX:
-       case SMBsesssetupX:
-       case SMBulogoffX:
-       case SMBntcreateX:
-               return true;
-               break;
-       default:
-               break;
-       }
-
-       return false;
-}
-
 /**
  * @brief Find the smb_cmd offset of the last command pushed
  * @param[in] buf      The buffer we're building up
@@ -435,7 +496,7 @@ static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx,
        req->async = tmp_reqs;
        req->num_async += 1;
 
-       req->async[req->num_async-1] = async_req_new(mem_ctx, ev);
+       req->async[req->num_async-1] = async_req_new(mem_ctx);
        if (req->async[req->num_async-1] == NULL) {
                DEBUG(0, ("async_req_new failed\n"));
                req->num_async -= 1;
@@ -552,6 +613,7 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
 void cli_chain_uncork(struct cli_state *cli)
 {
        struct cli_request *req = cli->chain_accumulator;
+       size_t smblen;
 
        SMB_ASSERT(req != NULL);
 
@@ -561,7 +623,19 @@ void cli_chain_uncork(struct cli_state *cli)
        cli->chain_accumulator = NULL;
 
        SSVAL(req->outbuf, smb_mid, req->mid);
-       smb_setlen((char *)req->outbuf, talloc_get_size(req->outbuf) - 4);
+
+       smblen = talloc_get_size(req->outbuf) - 4;
+
+       smb_setlen((char *)req->outbuf, smblen);
+
+       if (smblen > 0x1ffff) {
+               /*
+                * This is a POSIX 14 word large write. Overwrite just the
+                * size field, the '0xFFSMB' has been set by smb_setlen which
+                * _smb_setlen_large does not do.
+                */
+               _smb_setlen_large(((char *)req->outbuf), smblen);
+       }
 
        cli_calculate_sign_mac(cli, (char *)req->outbuf);
 
@@ -613,6 +687,7 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
 {
        struct async_req *result;
        bool uncork = false;
+       struct timeval endtime;
 
        if (cli->chain_accumulator == NULL) {
                if (!cli_chain_cork(cli, ev,
@@ -631,6 +706,14 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
                DEBUG(1, ("cli_request_chain failed\n"));
        }
 
+       endtime = timeval_current_ofs(0, cli->timeout * 1000);
+
+       if (!async_req_set_endtime(result, ev, endtime)) {
+               DEBUG(1, ("async_req_set_endtime failed\n"));
+               TALLOC_FREE(result);
+               return NULL;
+       }
+
        if (uncork) {
                cli_chain_uncork(cli);
        }
@@ -861,6 +944,8 @@ static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu)
 
        if (!cli_check_sign_mac(cli, pdu)) {
                DEBUG(10, ("cli_check_sign_mac failed\n"));
+               close(cli->fd);
+               cli->fd = -1;
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -874,7 +959,7 @@ static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu)
 
 static void handle_incoming_pdu(struct cli_state *cli)
 {
-       struct cli_request *req;
+       struct cli_request *req, *next;
        uint16_t mid;
        size_t raw_pdu_len, buf_len, pdu_len, rest_len;
        char *pdu;
@@ -936,6 +1021,12 @@ static void handle_incoming_pdu(struct cli_state *cli)
                }
        }
 
+       if ((raw_pdu_len == 4) && (CVAL(pdu, 0) == SMBkeepalive)) {
+               DEBUG(10, ("Got keepalive\n"));
+               TALLOC_FREE(pdu);
+               return;
+       }
+
        status = validate_smb_crypto(cli, pdu);
        if (!NT_STATUS_IS_OK(status)) {
                goto invalidate_requests;
@@ -991,8 +1082,11 @@ static void handle_incoming_pdu(struct cli_state *cli)
        DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n",
                   nt_errstr(status)));
 
-       for (req = cli->outstanding_requests; req; req = req->next) {
-               async_req_error(req->async[0], status);
+       for (req = cli->outstanding_requests; req; req = next) {
+               next = req->next;
+               if (req->num_async) {
+                       async_req_nterror(req->async[0], status);
+               }
        }
        return;
 }
@@ -1009,11 +1103,16 @@ static void cli_state_handler(struct event_context *event_ctx,
                              struct fd_event *event, uint16 flags, void *p)
 {
        struct cli_state *cli = (struct cli_state *)p;
-       struct cli_request *req;
+       struct cli_request *req, *next;
        NTSTATUS status;
 
        DEBUG(11, ("cli_state_handler called with flags %d\n", flags));
 
+       if (cli->fd == -1) {
+               status = NT_STATUS_CONNECTION_INVALID;
+               goto sock_error;
+       }
+
        if (flags & EVENT_FD_WRITE) {
                size_t to_send;
                ssize_t sent;
@@ -1115,13 +1214,20 @@ static void cli_state_handler(struct event_context *event_ctx,
        return;
 
  sock_error:
-       for (req = cli->outstanding_requests; req; req = req->next) {
-               int i;
-               for (i=0; i<req->num_async; i++) {
-                       async_req_error(req->async[i], status);
+
+       for (req = cli->outstanding_requests; req; req = next) {
+               int i, num_async;
+
+               next = req->next;
+               num_async = req->num_async;
+
+               for (i=0; i<num_async; i++) {
+                       async_req_nterror(req->async[i], status);
                }
        }
        TALLOC_FREE(cli->fd_event);
-       close(cli->fd);
-       cli->fd = -1;
+       if (cli->fd != -1) {
+               close(cli->fd);
+               cli->fd = -1;
+       }
 }