diff --git a/source/client/client.c b/source/client/client.c index 18b286324b6..1c0dff92c15 100644 --- a/source/client/client.c +++ b/source/client/client.c @@ -943,6 +943,7 @@ static int cmd_echo(void) TALLOC_CTX *ctx = talloc_tos(); char *num; char *data; + NTSTATUS status; if (!next_token_talloc(ctx, &cmd_ptr, &num, NULL) || !next_token_talloc(ctx, &cmd_ptr, &data, NULL)) { @@ -950,9 +951,10 @@ static int cmd_echo(void) return 1; } - if (!cli_echo(cli, atoi(num), (uint8 *)data, strlen(data))) { - d_printf("echo failed: %s\n", - nt_errstr(cli_get_nt_error(cli))); + status = cli_echo(cli, atoi(num), data_blob_const(data, strlen(data))); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("echo failed: %s\n", nt_errstr(status)); return 1; } @@ -4417,7 +4419,7 @@ static void readline_callback(void) { unsigned char garbage[16]; memset(garbage, 0xf0, sizeof(garbage)); - cli_echo(cli, 1, garbage, sizeof(garbage)); + cli_echo(cli, 1, data_blob_const(garbage, sizeof(garbage))); } } diff --git a/source/include/async_smb.h b/source/include/async_smb.h index 6a09bb6001e..ed42baef0d6 100644 --- a/source/include/async_smb.h +++ b/source/include/async_smb.h @@ -90,6 +90,10 @@ struct cli_request { ssize_t received; uint8_t *rcvbuf; } read; + struct { + DATA_BLOB data; + uint16_t num_echos; + } echo; } data; /** diff --git a/source/include/proto.h b/source/include/proto.h index f21cc2b17f0..abfc79024ad 100644 --- a/source/include/proto.h +++ b/source/include/proto.h @@ -4338,8 +4338,11 @@ void cli_sockopt(struct cli_state *cli, const char *options); uint16 cli_setpid(struct cli_state *cli, uint16 pid); bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive); bool cli_send_keepalive(struct cli_state *cli); -bool cli_echo(struct cli_state *cli, uint16 num_echos, - unsigned char *data, size_t length); +struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data); +NTSTATUS cli_echo_recv(struct async_req *req); +NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data); /* The following definitions come from libsmb/clierror.c */ diff --git a/source/libsmb/clientgen.c b/source/libsmb/clientgen.c index 2c0950de03a..239ba470a96 100644 --- a/source/libsmb/clientgen.c +++ b/source/libsmb/clientgen.c @@ -637,41 +637,153 @@ bool cli_send_keepalive(struct cli_state *cli) return true; } -/**************************************************************************** - Send/receive a SMBecho command: ping the server -****************************************************************************/ +/** + * @brief: Collect a echo reply + * @param[in] req The corresponding async request + * + * There might be more than one echo reply. This helper pulls the reply out of + * the data stream. If all expected replies have arrived, declare the + * async_req done. + */ -bool cli_echo(struct cli_state *cli, uint16 num_echos, - unsigned char *data, size_t length) +static void cli_echo_recv_helper(struct async_req *req) { - char *p; - int i; + struct cli_request *cli_req; + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; + NTSTATUS status; - SMB_ASSERT(length < 1024); - - memset(cli->outbuf,'\0',smb_size); - cli_set_message(cli->outbuf,1,length,true); - SCVAL(cli->outbuf,smb_com,SMBecho); - SSVAL(cli->outbuf,smb_tid,65535); - SSVAL(cli->outbuf,smb_vwv0,num_echos); - cli_setup_packet(cli); - p = smb_buf(cli->outbuf); - memcpy(p, data, length); - p += length; - - cli_setup_bcc(cli, p); - - cli_send_smb(cli); - - for (i=0; idata.echo.data.length) + || (memcmp(cli_req->data.echo.data.data, bytes, + num_bytes) != 0)) { + async_req_error(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + cli_req->data.echo.num_echos -= 1; + + if (cli_req->data.echo.num_echos == 0) { + client_set_trans_sign_state_off(cli_req->cli, cli_req->mid); + async_req_done(req); + return; + } + + return; +} + +/** + * @brief Send SMBEcho requests + * @param[in] mem_ctx The memory context to put the async_req on + * @param[in] ev The event context that will call us back + * @param[in] cli The connection to send the echo to + * @param[in] num_echos How many times do we want to get the reply? + * @param[in] data The data we want to get back + * @retval The async request + */ + +struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data) +{ + uint16_t vwv[1]; + uint8_t *data_copy; + struct async_req *result; + struct cli_request *req; + + SSVAL(vwv, 0, num_echos); + + data_copy = (uint8_t *)talloc_memdup(mem_ctx, data.data, data.length); + if (data_copy == NULL) { + return NULL; + } + + result = cli_request_send(mem_ctx, ev, cli, SMBecho, 0, 1, vwv, + data.length, data.data); + if (result == NULL) { + TALLOC_FREE(data_copy); + return NULL; + } + req = cli_request_get(result); + + client_set_trans_sign_state_on(cli, req->mid); + + req->data.echo.num_echos = num_echos; + req->data.echo.data.data = talloc_move(req, &data_copy); + req->data.echo.data.length = data.length; + + req->recv_helper.fn = cli_echo_recv_helper; + + return result; +} + +/** + * Get the result out from an echo request + * @param[in] req The async_req from cli_echo_send + * @retval Did the server reply correctly? + */ + +NTSTATUS cli_echo_recv(struct async_req *req) +{ + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; + } + + return NT_STATUS_OK; +} + +/** + * @brief Send/Receive SMBEcho requests + * @param[in] mem_ctx The memory context to put the async_req on + * @param[in] ev The event context that will call us back + * @param[in] cli The connection to send the echo to + * @param[in] num_echos How many times do we want to get the reply? + * @param[in] data The data we want to get back + * @retval Did the server reply correctly? + */ + +NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_echo_send(frame, ev, cli, num_echos, data); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = cli_echo_recv(req); + + fail: + TALLOC_FREE(frame); + return status; } diff --git a/source/torture/torture.c b/source/torture/torture.c index d159ffbac35..d8942e42b9b 100644 --- a/source/torture/torture.c +++ b/source/torture/torture.c @@ -4976,6 +4976,38 @@ static bool run_chain1(int dummy) return True; } +static bool run_cli_echo(int dummy) +{ + struct cli_state *cli; + struct event_context *ev = event_context_init(NULL); + struct async_req *req; + NTSTATUS status; + + printf("starting chain1 test\n"); + if (!torture_open_connection(&cli, 0)) { + return false; + } + cli_sockopt(cli, sockops); + + req = cli_echo_send(ev, ev, cli, 5, data_blob_const("hello", 5)); + if (req == NULL) { + d_printf("cli_echo_send failed\n"); + return false; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = cli_echo_recv(req); + d_printf("cli_echo returned %s\n", nt_errstr(status)); + + TALLOC_FREE(req); + + torture_close_connection(cli); + return NT_STATUS_IS_OK(status); +} + static bool run_local_substitute(int dummy) { bool ok = true; @@ -5474,6 +5506,7 @@ static struct { { "EATEST", run_eatest, 0}, { "SESSSETUP_BENCH", run_sesssetup_bench, 0}, { "CHAIN1", run_chain1, 0}, + { "CLI_ECHO", run_cli_echo, 0}, { "LOCAL-SUBSTITUTE", run_local_substitute, 0}, { "LOCAL-GENCACHE", run_local_gencache, 0}, { "LOCAL-RBTREE", run_local_rbtree, 0},