return req->transport->negotiate.max_xmit - req->out.size;
}
+struct raw_trans_state;
+
+struct raw_trans_ops {
+ bool (*format_first)(struct smbcli_request *req,
+ struct raw_trans_state *s);
+ bool (*create_next)(struct smbcli_request *req,
+ struct smbcli_request **req2,
+ struct raw_trans_state *s);
+ bool (*format_next)(struct smbcli_request *req,
+ struct smbcli_request *req2,
+ struct raw_trans_state *s);
+ bool (*parse_rep)(struct smbcli_request *req,
+ struct raw_trans_state *s);
+};
+
+struct raw_trans_state {
+ uint8_t command;
+
+// uint32_t num_req;
+ uint32_t params_left;
+ uint32_t data_left;
+
+ uint32_t num_rep;
+ uint32_t params_recvd;
+ uint32_t data_recvd;
+
+ struct {
+ uint8_t setup_count;
+ uint8_t setup_vwv_ofs;
+ uint32_t params_total;
+ DATA_BLOB params_chunk;
+ uint32_t params_ofs;
+ uint32_t params_disp;
+ uint32_t data_total;
+ DATA_BLOB data_chunk;
+ uint32_t data_ofs;
+ uint32_t data_disp;
+ } req;
+
+ struct {
+ uint8_t setup_count;
+ uint8_t setup_vwv_ofs;
+ uint32_t params_total;
+ uint32_t params_count;
+ uint32_t params_ofs;
+ uint32_t params_disp;
+ uint32_t data_total;
+ uint32_t data_count;
+ uint32_t data_ofs;
+ uint32_t data_disp;
+ } rep;
+
+ const struct raw_trans_ops *ops;
+ void *ops_private;
+
+ struct smb_nttrans io;
+};
+
+static enum smbcli_request_state raw_trans_recv_helper(struct smbcli_request *req);
+
+static NTSTATUS raw_trans_ship_first(struct smbcli_request *req,
+ struct smb_nttrans *parms,
+ const struct raw_trans_ops *ops,
+ void *ops_private)
+{
+ struct raw_trans_state *s;
+ size_t space_left;
+ uint32_t ofs;
+
+ s = talloc_zero(req, struct raw_trans_state);
+ if (!s) {
+ goto nomem;
+ }
+
+ s->ops = ops;
+ s->ops_private = ops_private;
+
+ s->req.params_total = parms->in.params.length;
+ s->req.data_total = parms->in.data.length;
+
+ s->params_left = s->req.params_total;
+ s->data_left = s->req.data_total;
+
+ ofs = PTR_DIFF(req->out.data + req->out.data_size, req->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req);
+
+/*TODO: align params */
+
+ s->req.params_disp = 0;
+ s->req.params_chunk.length = MIN(s->params_left, space_left);
+ s->req.params_chunk.data = parms->in.params.data + s->req.params_disp;
+ s->req.params_ofs = ofs;
+
+ s->params_left -= s->req.params_chunk.length;
+
+ if (s->params_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ s->io.in.params = data_blob_talloc(s, NULL, parms->in.params.length);
+ if (!s->io.in.params.data) {
+ goto nomem;
+ }
+ memcpy(s->io.in.params.data,
+ parms->in.params.data,
+ parms->in.params.length);
+ }
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= s->req.params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+/*TODO: align data */
+
+ s->req.data_disp = 0;
+ s->req.data_chunk.length = MIN(s->data_left, space_left);
+ s->req.data_chunk.data = parms->in.data.data + s->req.data_disp;
+ s->req.data_ofs = s->req.params_ofs + s->req.params_chunk.length;
+
+ s->data_left -= s->req.data_chunk.length;
+
+ if (s->data_left > 0) {
+ /* we copy the whole params block, if needed we can optimize that latter */
+ s->io.in.data = data_blob_talloc(s, NULL, parms->in.data.length);
+ if (!s->io.in.data.data) {
+ goto nomem;
+ }
+ memcpy(s->io.in.data.data,
+ parms->in.data.data,
+ parms->in.data.length);
+ }
+
+ if (!s->ops->format_first(req, s)) {
+ goto failed;
+ }
+
+ if (parms->in.setup_count) {
+ memcpy(req->out.vwv + s->req.setup_vwv_ofs,
+ parms->in.setup,
+ parms->in.setup_count * VWV(1));
+ }
+
+/*TODO: grow alloc */
+/*TODO: params align */
+ smbcli_req_append_blob(req, &s->req.params_chunk);
+/*TODO: data align */
+ smbcli_req_append_blob(req, &s->req.data_chunk);
+
+ /* add the helper which will check that all multi-part replies are
+ in before an async client callack will be issued */
+ req->recv_helper.fn = raw_trans_recv_helper;
+ req->recv_helper.private_data = s;
+
+ if (!smbcli_request_send(req)) {
+ return req->status;
+ }
+
+ return NT_STATUS_OK;
+failed:
+ return NT_STATUS_FOOBAR;
+nomem:
+ return NT_STATUS_NO_MEMORY;
+}
+
+static enum smbcli_request_state raw_trans_ship_next(struct smbcli_request *req,
+ struct raw_trans_state *s)
+{
+ struct smbcli_request *req2 = NULL;
+ size_t space_left;
+ uint32_t ofs;
+
+ if (!s->ops->create_next(req, &req2, s)) {
+ goto failed;
+ }
+
+ ofs = PTR_DIFF(req2->out.data + req2->out.data_size, req2->out.hdr);
+
+ /* see how much bytes of the params block we can ship in the first request */
+ space_left = raw_trans_space_left(req2);
+
+/*TODO: align params */
+
+ s->req.params_disp = s->io.in.params.length - s->params_left;
+ s->req.params_chunk.length = MIN(s->params_left, space_left);
+ s->req.params_chunk.data = s->io.in.params.data + s->req.params_disp;
+ s->req.params_ofs = ofs;
+
+ s->params_left -= s->req.params_chunk.length;
+
+ /* see how much bytes of the data block we can ship in the first request */
+ space_left -= s->req.params_chunk.length;
+
+#if TORTURE_TRANS_DATA
+ if (space_left > 1) {
+ space_left /= 2;
+ }
+#endif
+
+/*TODO: align data */
+
+ s->req.data_disp = s->io.in.data.length - s->data_left;
+ s->req.data_chunk.length = MIN(s->data_left, space_left);
+ s->req.data_chunk.data = s->io.in.data.data + s->req.data_disp;
+ s->req.data_ofs = s->req.params_ofs + s->req.params_chunk.length;
+
+ s->data_left -= s->req.data_chunk.length;
+
+ if (!s->ops->format_next(req, req2, s)) {
+ goto failed;
+ }
+
+/*TODO: grow alloc */
+/*TODO: params align */
+ smbcli_req_append_blob(req2, &s->req.params_chunk);
+/*TODO: data align */
+ smbcli_req_append_blob(req2, &s->req.data_chunk);
+
+ /*
+ * it's a one way request but we need
+ * the seq_num, so we destroy req2 by hand
+ */
+ if (!smbcli_request_send(req2)) {
+ goto failed;
+ }
+
+ req->seq_num = req2->seq_num;
+ smbcli_request_destroy(req2);
+
+ return SMBCLI_REQUEST_RECV;
+
+failed:
+ if (req2) {
+ req->status = smbcli_request_destroy(req2);
+ }
+ return SMBCLI_REQUEST_ERROR;
+}
+
+static enum smbcli_request_state raw_trans_ship_rest(struct smbcli_request *req,
+ struct raw_trans_state *s)
+{
+ enum smbcli_request_state ret = SMBCLI_REQUEST_ERROR;
+
+ while (s->params_left > 0 || s->data_left > 0) {
+ ret = raw_trans_ship_next(req, s);
+ if (ret != SMBCLI_REQUEST_RECV) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This helper returns SMBCLI_REQUEST_RECV until all data has arrived
+ */
+static enum smbcli_request_state raw_trans_recv_helper(struct smbcli_request *req)
+{
+ struct raw_trans_state *s = talloc_get_type(req->recv_helper.private_data,
+ struct raw_trans_state);
+
+ /*
+ * An NT RPC pipe call can return ERRDOS, ERRmoredata
+ * to a trans call. This is not an error and should not
+ * be treated as such.
+ */
+ if (smbcli_request_is_error(req)) {
+ goto failed;
+ }
+
+ if (s->params_left > 0 || s->data_left > 0) {
+ SMBCLI_CHECK_WCT(req, 0);
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ return raw_trans_ship_rest(req, s);
+ }
+
+ s->num_rep++;
+
+ if (!s->ops->parse_rep(req, s)) {
+ goto failed;
+ }
+
+ /*
+ * setup_count should only be present in the first reply
+ */
+ if (s->num_rep > 1 && s->rep.setup_count != 0) {
+ DEBUG(1,("raw_trans_recv_helper: setup[%u] num_rep[%u]\n",
+ s->rep.setup_count, s->num_rep));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->rep.setup_count > s->io.in.max_setup) {
+ DEBUG(1,("raw_trans_recv_helper: setup[%u] > max[%u]\n",
+ s->rep.setup_count, s->io.in.max_setup));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->rep.params_total > s->io.in.max_param) {
+ DEBUG(1,("raw_trans_recv_helper: params[%u] > max[%u]\n",
+ s->rep.params_total, s->io.in.max_param));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->rep.data_total > s->io.in.max_data) {
+ DEBUG(1,("raw_trans_recv_helper: data[%u] > max[%u]\n",
+ s->rep.data_total, s->io.in.max_data));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->num_rep == 1) {
+ if (s->rep.setup_count > 0 &&
+ s->io.in.max_setup != s->io.out.setup_count) {
+ s->io.out.setup = talloc_array(s, uint8_t,
+ s->rep.setup_count * VWV(1));
+ if (!s->io.out.setup) {
+ goto nomem;
+ }
+ }
+ s->io.out.setup_count = s->rep.setup_count;
+
+ if (s->rep.params_total > 0 &&
+ s->io.in.max_param != s->io.out.params.length) {
+ s->io.out.params = data_blob_talloc(s, NULL,
+ s->rep.params_total);
+ if (!s->io.out.params.data) {
+ goto nomem;
+ }
+ }
+ s->io.out.params.length = s->rep.params_total;
+
+ if (s->rep.data_total > 0 &&
+ s->io.in.max_data != s->io.out.data.length) {
+ s->io.out.data = data_blob_talloc(s, NULL,
+ s->rep.data_total);
+ if (!s->io.out.data.data) {
+ goto nomem;
+ }
+ }
+ s->io.out.data.length = s->rep.data_total;
+ }
+
+ if (s->rep.params_total > s->io.out.params.length) {
+ /* this must *only* shrink */
+ DEBUG(1,("raw_trans_recv_helper: params expanded %u > %u!\n",
+ s->rep.params_total, (unsigned int)s->io.out.params.length));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+ s->io.out.params.length = s->rep.params_total;
+
+ if (s->rep.data_total > s->io.out.data.length) {
+ /* this must *only* shrink */
+ DEBUG(1,("raw_trans_recv_helper: data expanded %u > %u!\n",
+ s->rep.data_total, (unsigned int)s->io.out.data.length));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+ s->io.out.data.length = s->rep.data_total;
+
+ if (s->rep.params_disp + s->rep.params_count > s->rep.params_total) {
+ DEBUG(1,("raw_trans_recv_helper: params overflow %u + %u > %u\n",
+ s->rep.params_disp, s->rep.params_count, s->rep.params_total));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (raw_trans_oob(req, s->rep.params_ofs, s->rep.params_count)) {
+ DEBUG(1,("raw_trans_recv_helper: params oob o[%u] c[%u]\n",
+ s->rep.params_ofs, s->rep.params_count));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->rep.data_disp + s->rep.data_count > s->rep.data_total) {
+ DEBUG(1,("raw_trans_recv_helper: data overflow %u + %u > %u\n",
+ s->rep.data_disp, s->rep.data_count, s->rep.data_total));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (raw_trans_oob(req, s->rep.data_ofs, s->rep.data_count)) {
+ DEBUG(1,("raw_trans_recv_helper: data oob o[%u] c[%u]\n",
+ s->rep.data_ofs, s->rep.data_count));
+ req->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto failed;
+ }
+
+ if (s->rep.setup_count) {
+ memcpy(s->io.out.setup,
+ req->in.vwv + s->rep.setup_vwv_ofs,
+ s->rep.setup_count * VWV(1));
+ }
+
+ if (s->rep.params_count) {
+ memcpy(s->io.out.params.data + s->rep.params_disp,
+ req->in.hdr + s->rep.params_ofs,
+ s->rep.params_count);
+ }
+
+ if (s->rep.data_count) {
+ memcpy(s->io.out.data.data + s->rep.data_disp,
+ req->in.hdr + s->rep.data_ofs,
+ s->rep.data_count);
+ }
+
+ s->params_recvd += s->rep.params_count;
+ s->data_recvd += s->rep.data_count;
+
+ if (s->params_recvd < s->rep.params_total ||
+ s->data_recvd < s->rep.data_total) {
+
+ /* we don't need the in buffer any more */
+ talloc_free(req->in.buffer);
+ ZERO_STRUCT(req->in);
+
+ /* we still wait for more data */
+ DEBUG(10,("raw_trans_recv_helper: more data needed\n"));
+ return SMBCLI_REQUEST_RECV;
+ }
+
+ DEBUG(10,("raw_trans_recv_helper: done\n"));
+ return SMBCLI_REQUEST_DONE;
+
+nomem:
+ req->status = NT_STATUS_NO_MEMORY;
+failed:
+ return SMBCLI_REQUEST_ERROR;
+}
+
struct smb_raw_trans2_recv_state {
uint8_t command;
uint32_t params_total;