mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
Activate code to enable chained requests
Add the CHAIN1 torture test
This commit is contained in:
parent
b4c539ba04
commit
82992d74a9
@ -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?
|
||||
*/
|
||||
struct async_req *async;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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},
|
||||
|
Loading…
Reference in New Issue
Block a user