raw_transabstraction...
authorStefan Metzmacher <metze@samba.org>
Thu, 10 Jul 2008 13:46:29 +0000 (15:46 +0200)
committerStefan Metzmacher <metze@samba.org>
Thu, 14 Aug 2008 11:35:34 +0000 (13:35 +0200)
source/libcli/raw/rawtrans.c

index 2f529863dc94171b1e93ce02ebcca9d5b58b9870..b225a35a3c77d757e683b8b4ae9ef95d21808929 100644 (file)
@@ -58,6 +58,447 @@ static size_t raw_trans_space_left(struct smbcli_request *req)
        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;