*/
#include "includes.h"
-
-/*
- * 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
- * @retval The error, converted to NTSTATUS
- */
-
-NTSTATUS cli_pull_error(char *buf)
+#include "libsmb/libsmb.h"
+#include "../lib/async_req/async_sock.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "../lib/util/tevent_unix.h"
+#include "async_smb.h"
+#include "smb_crypt.h"
+#include "libsmb/nmblib.h"
+#include "read_smb.h"
+
+static NTSTATUS cli_pull_raw_error(const uint8_t *buf)
{
uint32_t flags2 = SVAL(buf, smb_flg2);
return NT_STATUS(IVAL(buf, smb_rcls));
}
- /* if the client uses dos errors, but there is no error,
- we should return no error here, otherwise it looks
- like an unknown bad NT_STATUS. jmcd */
- if (CVAL(buf, smb_rcls) == 0)
- return NT_STATUS_OK;
-
return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err));
}
-/**
- * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
- * @param[in] cli The client connection that just received an error
- * @param[in] status The error to set on "cli"
- */
-
-void cli_set_error(struct cli_state *cli, NTSTATUS status)
-{
- uint32_t flags2 = SVAL(cli->inbuf, smb_flg2);
-
- if (NT_STATUS_IS_DOS(status)) {
- SSVAL(cli->inbuf, smb_flg2,
- flags2 & ~FLAGS2_32_BIT_ERROR_CODES);
- SCVAL(cli->inbuf, smb_rcls, NT_STATUS_DOS_CLASS(status));
- SSVAL(cli->inbuf, smb_err, NT_STATUS_DOS_CODE(status));
- return;
- }
-
- SSVAL(cli->inbuf, smb_flg2, flags2 | FLAGS2_32_BIT_ERROR_CODES);
- SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(status));
- return;
-}
-
/**
* Figure out if there is an andx command behind the current one
* @param[in] buf The smb buffer to look at
uint8_t *inbuf;
uint32_t seqnum;
int chain_num;
+ int chain_length;
struct tevent_req **chained_requests;
};
int num_pending = talloc_array_length(cli->pending);
int i;
+ if (state->mid != 0) {
+ /*
+ * This is a [nt]trans[2] request which waits
+ * for more than one reply.
+ */
+ return;
+ }
+
if (num_pending == 1) {
/*
* The pending read_smb tevent_req is a child of
/*
* Remove ourselves from the cli->pending array
*/
- if (num_pending > 1) {
- cli->pending[i] = cli->pending[num_pending-1];
- }
+ cli->pending[i] = cli->pending[num_pending-1];
/*
* No NULL check here, we're shrinking by sizeof(void *), and
static int cli_smb_req_destructor(struct tevent_req *req)
{
+ struct cli_smb_state *state = tevent_req_data(
+ req, struct cli_smb_state);
+ /*
+ * Make sure we really remove it from
+ * the pending array on destruction.
+ */
+ state->mid = 0;
cli_smb_req_unset_pending(req);
return 0;
}
state->mid = mid;
}
+uint32_t cli_smb_req_seqnum(struct tevent_req *req)
+{
+ struct cli_smb_state *state = tevent_req_data(
+ req, struct cli_smb_state);
+ return state->seqnum;
+}
+
+void cli_smb_req_set_seqnum(struct tevent_req *req, uint32_t seqnum)
+{
+ struct cli_smb_state *state = tevent_req_data(
+ req, struct cli_smb_state);
+ state->seqnum = seqnum;
+}
+
static size_t iov_len(const struct iovec *iov, int count)
{
size_t result = 0;
}
state->iov_count = iov_count + 3;
- endtime = timeval_current_ofs(0, cli->timeout * 1000);
- if (!tevent_req_set_endtime(result, ev, endtime)) {
- tevent_req_nomem(NULL, result);
+ if (cli->timeout) {
+ endtime = timeval_current_ofs_msec(cli->timeout);
+ if (!tevent_req_set_endtime(result, ev, endtime)) {
+ tevent_req_oom(result);
+ }
}
return result;
}
if (state->mid != 0) {
SSVAL(iov[0].iov_base, smb_mid, state->mid);
} else {
- SSVAL(iov[0].iov_base, smb_mid, cli_alloc_mid(state->cli));
+ uint16_t mid = cli_alloc_mid(state->cli);
+ SSVAL(iov[0].iov_base, smb_mid, mid);
}
smb_setlen((char *)iov[0].iov_base, iov_len(iov, iov_count) - 4);
if (buf == NULL) {
return NT_STATUS_NO_MEMORY;
}
- status = cli_encrypt_message(state->cli, (char *)buf,
- &enc_buf);
+ status = common_encrypt_buffer(state->cli->trans_enc_state,
+ (char *)buf, &enc_buf);
TALLOC_FREE(buf);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("Error in encrypting client message: %s\n",
struct iovec iov;
NTSTATUS status;
- iov.iov_base = CONST_DISCARD(void *, bytes);
+ iov.iov_base = discard_const_p(void, bytes);
iov.iov_len = num_bytes;
req = cli_smb_req_create(mem_ctx, ev, cli, smb_command,
subreq, struct cli_state);
struct tevent_req *req;
struct cli_smb_state *state;
- struct tevent_context *ev;
NTSTATUS status;
uint8_t *inbuf;
ssize_t received;
req = cli->pending[i];
state = tevent_req_data(req, struct cli_smb_state);
- ev = state->ev;
if (!oplock_break /* oplock breaks are not signed */
&& !cli_check_sign_mac(cli, (char *)inbuf, state->seqnum+1)) {
DEBUG(10, ("cli_check_sign_mac failed\n"));
TALLOC_FREE(inbuf);
status = NT_STATUS_ACCESS_DENIED;
+ close(cli->fd);
+ cli->fd = -1;
goto fail;
}
if (state->chained_requests == NULL) {
state->inbuf = talloc_move(state, &inbuf);
talloc_set_destructor(req, NULL);
- cli_smb_req_destructor(req);
+ cli_smb_req_unset_pending(req);
+ state->chain_num = 0;
+ state->chain_length = 1;
tevent_req_done(req);
} else {
struct tevent_req **chain = talloc_move(
cli_smb_state);
state->inbuf = inbuf;
state->chain_num = i;
+ state->chain_length = num_chained;
tevent_req_done(chain[i]);
}
TALLOC_FREE(inbuf);
while (talloc_array_length(cli->pending) > 0) {
req = cli->pending[0];
talloc_set_destructor(req, NULL);
- cli_smb_req_destructor(req);
+ cli_smb_req_unset_pending(req);
tevent_req_nterror(req, status);
}
}
-NTSTATUS cli_smb_recv(struct tevent_req *req, uint8_t min_wct,
- uint8_t *pwct, uint16_t **pvwv,
+NTSTATUS cli_smb_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
+ uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
uint32_t *pnum_bytes, uint8_t **pbytes)
{
struct cli_smb_state *state = tevent_req_data(
}
if (state->inbuf == NULL) {
+ if (min_wct != 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (pinbuf) {
+ *pinbuf = NULL;
+ }
+ if (pwct) {
+ *pwct = 0;
+ }
+ if (pvwv) {
+ *pvwv = NULL;
+ }
+ if (pnum_bytes) {
+ *pnum_bytes = 0;
+ }
+ if (pbytes) {
+ *pbytes = NULL;
+ }
/* This was a request without a reply */
return NT_STATUS_OK;
}
cmd = CVAL(state->inbuf, wct_ofs + 1);
}
- status = cli_pull_error((char *)state->inbuf);
+ state->cli->raw_status = cli_pull_raw_error(state->inbuf);
+ if (NT_STATUS_IS_DOS(state->cli->raw_status)) {
+ uint8_t eclass = NT_STATUS_DOS_CLASS(state->cli->raw_status);
+ uint16_t ecode = NT_STATUS_DOS_CODE(state->cli->raw_status);
+ /*
+ * TODO: is it really a good idea to do a mapping here?
+ *
+ * The old cli_pull_error() also does it, so I do not change
+ * the behavior yet.
+ */
+ status = dos_to_ntstatus(eclass, ecode);
+ } else {
+ status = state->cli->raw_status;
+ }
if (!have_andx_command((char *)state->inbuf, wct_ofs)) {
if (pbytes != NULL) {
*pbytes = (uint8_t *)state->inbuf + bytes_offset + 2;
}
+ if ((mem_ctx != NULL) && (pinbuf != NULL)) {
+ if (state->chain_num == state->chain_length-1) {
+ *pinbuf = talloc_move(mem_ctx, &state->inbuf);
+ } else {
+ *pinbuf = state->inbuf;
+ }
+ }
return status;
}
return NT_STATUS_OK;
}
-uint8_t *cli_smb_inbuf(struct tevent_req *req)
-{
- struct cli_smb_state *state = tevent_req_data(
- req, struct cli_smb_state);
- return state->inbuf;
-}
-
bool cli_has_async_calls(struct cli_state *cli)
{
return ((tevent_queue_length(cli->outgoing) != 0)
uint16_t *vwv;
uint32_t num_bytes;
uint8_t *bytes;
+ uint8_t *inbuf;
NTSTATUS status;
- status = cli_smb_recv(subreq, 8, &wct, &vwv, &num_bytes, &bytes);
+ status = cli_smb_recv(subreq, state, &inbuf, 8, &wct, &vwv,
+ &num_bytes, &bytes);
+ TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(subreq);
tevent_req_nterror(req, status);
return;
}
*plevel = state->level;
return NT_STATUS_OK;
}
+
+
+struct cli_session_request_state {
+ struct tevent_context *ev;
+ int sock;
+ uint32 len_hdr;
+ struct iovec iov[3];
+ uint8_t nb_session_response;
+};
+
+static void cli_session_request_sent(struct tevent_req *subreq);
+static void cli_session_request_recvd(struct tevent_req *subreq);
+
+struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int sock,
+ const struct nmb_name *called,
+ const struct nmb_name *calling)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_session_request_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_session_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->sock = sock;
+
+ state->iov[1].iov_base = name_mangle(
+ state, called->name, called->name_type);
+ if (tevent_req_nomem(state->iov[1].iov_base, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->iov[1].iov_len = name_len(
+ (unsigned char *)state->iov[1].iov_base,
+ talloc_get_size(state->iov[1].iov_base));
+
+ state->iov[2].iov_base = name_mangle(
+ state, calling->name, calling->name_type);
+ if (tevent_req_nomem(state->iov[2].iov_base, req)) {
+ return tevent_req_post(req, ev);
+ }
+ state->iov[2].iov_len = name_len(
+ (unsigned char *)state->iov[2].iov_base,
+ talloc_get_size(state->iov[2].iov_base));
+
+ _smb_setlen(((char *)&state->len_hdr),
+ state->iov[1].iov_len + state->iov[2].iov_len);
+ SCVAL((char *)&state->len_hdr, 0, 0x81);
+
+ state->iov[0].iov_base = &state->len_hdr;
+ state->iov[0].iov_len = sizeof(state->len_hdr);
+
+ subreq = writev_send(state, ev, NULL, sock, true, state->iov, 3);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_session_request_sent, req);
+ return req;
+}
+
+static void cli_session_request_sent(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+ ssize_t ret;
+ int err;
+
+ ret = writev_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ subreq = read_smb_send(state, state->ev, state->sock);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_session_request_recvd, req);
+}
+
+static void cli_session_request_recvd(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+ uint8_t *buf;
+ ssize_t ret;
+ int err;
+
+ ret = read_smb_recv(subreq, talloc_tos(), &buf, &err);
+ TALLOC_FREE(subreq);
+
+ if (ret < 4) {
+ ret = -1;
+ err = EIO;
+ }
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ /*
+ * In case of an error there is more information in the data
+ * portion according to RFC1002. We're not subtle enough to
+ * respond to the different error conditions, so drop the
+ * error info here.
+ */
+ state->nb_session_response = CVAL(buf, 0);
+ tevent_req_done(req);
+}
+
+bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp)
+{
+ struct cli_session_request_state *state = tevent_req_data(
+ req, struct cli_session_request_state);
+
+ if (tevent_req_is_unix_error(req, err)) {
+ return false;
+ }
+ *resp = state->nb_session_response;
+ return true;
+}