summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source3/include/async_smb.h21
-rw-r--r--source3/include/client.h8
-rw-r--r--source3/libsmb/async_smb.c203
-rw-r--r--source3/torture/torture.c80
4 files changed, 133 insertions, 179 deletions
diff --git a/source3/include/async_smb.h b/source3/include/async_smb.h
index 031ab233dd4..1053de29422 100644
--- a/source3/include/async_smb.h
+++ b/source3/include/async_smb.h
@@ -22,6 +22,13 @@
#include "includes.h"
+/**
+ * struct cli_request is the state holder for an async client request we sent
+ * to the server. It can consist of more than one struct async_req that we
+ * have to server if the application did a cli_chain_cork() and
+ * cli_chain_uncork()
+ */
+
struct cli_request {
/**
* "prev" and "next" form the doubly linked list in
@@ -30,9 +37,15 @@ struct cli_request {
struct cli_request *prev, *next;
/**
- * "our" struct async_req;
+ * num_async: How many chained requests do we serve?
+ */
+ int num_async;
+
+ /**
+ * async: This is the list of chained requests that were queued up by
+ * cli_request_chain before we sent out this request
*/
- struct async_req *async;
+ struct async_req **async;
/**
* The client connection for this request
@@ -92,6 +105,10 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
uint8_t wct, const uint16_t *vwv,
uint16_t num_bytes, const uint8_t *bytes);
+bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
+ size_t size_hint);
+void cli_chain_uncork(struct cli_state *cli);
+
/*
* Convenience function to get the SMB part out of an async_req
*/
diff --git a/source3/include/client.h b/source3/include/client.h
index 6a6e1a2faa0..9b564fc48e7 100644
--- a/source3/include/client.h
+++ b/source3/include/client.h
@@ -208,7 +208,8 @@ struct cli_state {
* fd_event is around while we have async requests outstanding or are
* building a chained request.
*
- * (fd_event!=NULL) && (outstanding_request!=NULL)
+ * (fd_event!=NULL) &&
+ * ((outstanding_request!=NULL)||(chain_accumulator!=NULL))
*
* should always be true, as well as the reverse: If both cli_request
* pointers are NULL, no fd_event is around.
@@ -220,6 +221,11 @@ struct cli_state {
* A linked list of requests that are waiting for a reply
*/
struct cli_request *outstanding_requests;
+
+ /**
+ * The place to build up the list of chained requests.
+ */
+ struct cli_request *chain_accumulator;
};
typedef struct file_info {
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c
index e7641474328..4d6c32edfa2 100644
--- a/source3/libsmb/async_smb.c
+++ b/source3/libsmb/async_smb.c
@@ -135,8 +135,6 @@ static int cli_request_destructor(struct cli_request *req)
return 0;
}
-#if 0
-
/**
* Is the SMB command able to hold an AND_X successor
* @param[in] cmd The SMB command in question
@@ -667,176 +665,6 @@ NTSTATUS cli_pull_reply(struct async_req *req,
return NT_STATUS_OK;
}
-#endif
-
-/*
- * Create a fresh async smb request
- */
-
-static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx,
- struct event_context *ev,
- struct cli_state *cli,
- uint8_t num_words, size_t num_bytes,
- struct cli_request **preq)
-{
- struct async_req *result;
- struct cli_request *cli_req;
- size_t bufsize = smb_size + num_words * 2 + num_bytes;
-
- result = async_req_new(mem_ctx, ev);
- if (result == NULL) {
- return NULL;
- }
-
- cli_req = (struct cli_request *)talloc_size(
- result, sizeof(*cli_req) + bufsize);
- if (cli_req == NULL) {
- TALLOC_FREE(result);
- return NULL;
- }
- talloc_set_name_const(cli_req, "struct cli_request");
- result->private_data = cli_req;
- result->print = cli_request_print;
-
- cli_req->async = result;
- cli_req->cli = cli;
- cli_req->outbuf = ((char *)cli_req + sizeof(*cli_req));
- cli_req->sent = 0;
- cli_req->mid = cli_new_mid(cli);
- cli_req->inbuf = NULL;
- cli_req->enc_state = NULL;
-
- SCVAL(cli_req->outbuf, smb_wct, num_words);
- SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes);
-
- DLIST_ADD_END(cli->outstanding_requests, cli_req,
- struct cli_request *);
- talloc_set_destructor(cli_req, cli_request_destructor);
-
- DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid));
-
- *preq = cli_req;
- return result;
-}
-
-/*
- * Ship a new smb request to the server
- */
-struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
- struct event_context *ev,
- struct cli_state *cli,
- uint8_t smb_command,
- uint8_t additional_flags,
- uint8_t wct, const uint16_t *vwv,
- uint16_t num_bytes, const uint8_t *bytes)
-{
- struct async_req *result;
- struct cli_request *req;
-
- if (cli->fd_event == NULL) {
- SMB_ASSERT(cli->outstanding_requests == NULL);
- cli->fd_event = event_add_fd(ev, cli, cli->fd,
- EVENT_FD_READ,
- cli_state_handler, cli);
- if (cli->fd_event == NULL) {
- return NULL;
- }
- }
-
- result = cli_request_new(mem_ctx, ev, cli, wct, num_bytes, &req);
- if (result == NULL) {
- DEBUG(0, ("cli_request_new failed\n"));
- TALLOC_FREE(cli->fd_event);
- return NULL;
- }
-
- cli_set_message(req->outbuf, wct, num_bytes, false);
- SCVAL(req->outbuf, smb_com, smb_command);
- SSVAL(req->outbuf, smb_tid, cli->cnum);
- cli_setup_packet_buf(cli, req->outbuf);
-
- memcpy(req->outbuf + smb_vwv0, vwv, sizeof(uint16_t) * wct);
- memcpy(smb_buf(req->outbuf), bytes, num_bytes);
- SSVAL(req->outbuf, smb_mid, req->mid);
- SCVAL(cli->outbuf, smb_flg,
- CVAL(cli->outbuf,smb_flg) | additional_flags);
-
- cli_calculate_sign_mac(cli, req->outbuf);
-
- if (cli_encryption_on(cli)) {
- NTSTATUS status;
- char *enc_buf;
-
- status = cli_encrypt_message(cli, req->outbuf, &enc_buf);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0, ("Error in encrypting client message. "
- "Error %s\n", nt_errstr(status)));
- TALLOC_FREE(req);
- return NULL;
- }
- req->outbuf = enc_buf;
- req->enc_state = cli->trans_enc_state;
- }
-
- event_fd_set_writeable(cli->fd_event);
-
- return result;
-}
-
-/**
- * @brief Pull reply data out of a request
- * @param[in] req The request that we just received a reply for
- * @param[out] pwct How many words did the server send?
- * @param[out] pvwv The words themselves
- * @param[out] pnum_bytes How many bytes did the server send?
- * @param[out] pbytes The bytes themselves
- * @retval Was the reply formally correct?
- */
-
-NTSTATUS cli_pull_reply(struct async_req *req,
- uint8_t *pwct, uint16_t **pvwv,
- uint16_t *pnum_bytes, uint8_t **pbytes)
-{
- struct cli_request *cli_req = cli_request_get(req);
- uint8_t wct, cmd;
- uint16_t num_bytes;
- size_t wct_ofs, bytes_offset;
- NTSTATUS status;
-
- status = cli_pull_error(cli_req->inbuf);
-
- if (NT_STATUS_IS_ERR(status)) {
- cli_set_error(cli_req->cli, status);
- return status;
- }
-
- cmd = CVAL(cli_req->inbuf, smb_com);
- wct_ofs = smb_wct;
-
- wct = CVAL(cli_req->inbuf, wct_ofs);
-
- bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t);
- num_bytes = SVAL(cli_req->inbuf, bytes_offset);
-
- /*
- * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
- * is a 16-bit value. So bytes_offset being size_t should be far from
- * wrapping.
- */
-
- if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf))
- || (bytes_offset > 0xffff)) {
- return NT_STATUS_INVALID_NETWORK_RESPONSE;
- }
-
- *pwct = wct;
- *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1);
- *pnum_bytes = num_bytes;
- *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2;
-
- return NT_STATUS_OK;
-}
-
/**
* Convenience function to get the SMB part out of an async_req
* @param[in] req The request to look at
@@ -862,8 +690,11 @@ static void handle_incoming_pdu(struct cli_state *cli)
uint16_t mid;
size_t raw_pdu_len, buf_len, pdu_len, rest_len;
char *pdu;
+ int i;
NTSTATUS status;
+ int num_async;
+
/*
* The encrypted PDU len might differ from the unencrypted one
*/
@@ -976,7 +807,24 @@ static void handle_incoming_pdu(struct cli_state *cli)
req->inbuf = talloc_move(req, &pdu);
- async_req_done(req->async);
+ /*
+ * Freeing the last async_req will free the req (see
+ * cli_async_req_destructor). So make a copy of req->num_async, we
+ * can't reference it in the last round.
+ */
+
+ num_async = req->num_async;
+
+ for (i=0; i<num_async; i++) {
+ /**
+ * A request might have been talloc_free()'ed before we arrive
+ * here. It will have removed itself from req->async via its
+ * destructor cli_async_req_destructor().
+ */
+ if (req->async[i] != NULL) {
+ async_req_done(req->async[i]);
+ }
+ }
return;
invalidate_requests:
@@ -985,7 +833,7 @@ static void handle_incoming_pdu(struct cli_state *cli)
nt_errstr(status)));
for (req = cli->outstanding_requests; req; req = req->next) {
- async_req_error(req->async, status);
+ async_req_error(req->async[0], status);
}
return;
}
@@ -1101,8 +949,11 @@ static void cli_state_handler(struct event_context *event_ctx,
sock_error:
for (req = cli->outstanding_requests; req; req = req->next) {
- req->async->state = ASYNC_REQ_ERROR;
- req->async->status = map_nt_error_from_unix(errno);
+ int i;
+ for (i=0; i<req->num_async; i++) {
+ req->async[i]->state = ASYNC_REQ_ERROR;
+ req->async[i]->status = map_nt_error_from_unix(errno);
+ }
}
TALLOC_FREE(cli->fd_event);
close(cli->fd);
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 75a5b30e304..d159ffbac35 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -4897,6 +4897,85 @@ static bool subst_test(const char *str, const char *user, const char *domain,
return result;
}
+static void chain1_open_completion(struct async_req *req)
+{
+ int fnum;
+ NTSTATUS status;
+
+ status = cli_open_recv(req, &fnum);
+ TALLOC_FREE(req);
+
+ d_printf("cli_open_recv returned %s: %d\n",
+ nt_errstr(status),
+ NT_STATUS_IS_OK(status) ? fnum : -1);
+}
+
+static void chain1_read_completion(struct async_req *req)
+{
+ NTSTATUS status;
+ ssize_t received;
+ uint8_t *rcvbuf;
+
+ status = cli_read_andx_recv(req, &received, &rcvbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(req);
+ d_printf("cli_read_andx_recv returned %s\n",
+ nt_errstr(status));
+ return;
+ }
+
+ d_printf("got %d bytes: %.*s\n", (int)received, (int)received,
+ (char *)rcvbuf);
+ TALLOC_FREE(req);
+}
+
+static void chain1_close_completion(struct async_req *req)
+{
+ NTSTATUS status;
+
+ status = cli_close_recv(req);
+ *((bool *)(req->async.priv)) = true;
+
+ TALLOC_FREE(req);
+
+ d_printf("cli_close returned %s\n", nt_errstr(status));
+}
+
+static bool run_chain1(int dummy)
+{
+ struct cli_state *cli1;
+ struct event_context *evt = event_context_init(NULL);
+ struct async_req *reqs[4];
+ bool done = false;
+
+ printf("starting chain1 test\n");
+ if (!torture_open_connection(&cli1, 0)) {
+ return False;
+ }
+
+ cli_sockopt(cli1, sockops);
+
+ cli_chain_cork(cli1, evt, 0);
+ reqs[0] = cli_open_send(talloc_tos(), evt, cli1, "\\test",
+ O_CREAT|O_RDWR, 0);
+ reqs[0]->async.fn = chain1_open_completion;
+ reqs[1] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 0, 10);
+ reqs[1]->async.fn = chain1_read_completion;
+ reqs[2] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 1, 10);
+ reqs[2]->async.fn = chain1_read_completion;
+ reqs[3] = cli_close_send(talloc_tos(), evt, cli1, 0);
+ reqs[3]->async.fn = chain1_close_completion;
+ reqs[3]->async.priv = (void *)&done;
+ cli_chain_uncork(cli1);
+
+ while (!done) {
+ event_loop_once(evt);
+ }
+
+ torture_close_connection(cli1);
+ return True;
+}
+
static bool run_local_substitute(int dummy)
{
bool ok = true;
@@ -5394,6 +5473,7 @@ static struct {
{"FDSESS", run_fdsesstest, 0},
{ "EATEST", run_eatest, 0},
{ "SESSSETUP_BENCH", run_sesssetup_bench, 0},
+ { "CHAIN1", run_chain1, 0},
{ "LOCAL-SUBSTITUTE", run_local_substitute, 0},
{ "LOCAL-GENCACHE", run_local_gencache, 0},
{ "LOCAL-RBTREE", run_local_rbtree, 0},