dcerpc server output now copes with the client blocking part way
authorAndrew Tridgell <tridge@samba.org>
Tue, 20 Jan 2004 05:54:17 +0000 (05:54 +0000)
committerAndrew Tridgell <tridge@samba.org>
Tue, 20 Jan 2004 05:54:17 +0000 (05:54 +0000)
through a read. This happens to also avoid a memcpy on output for
dcerpc over tcp.

source/ntvfs/ipc/vfs_ipc.c
source/rpc_server/dcerpc_server.c
source/rpc_server/dcerpc_tcp.c

index cd300b6589ebd40dd97dad026e76d653db535f24..c6d5a529605677d157cb32632e05d0694d71b525 100644 (file)
@@ -388,7 +388,7 @@ static NTSTATUS ipc_read(struct request_context *req, union smb_read *rd)
                return NT_STATUS_INVALID_HANDLE;
        }
 
-       status = dcesrv_output(p->dce_conn, &data);
+       status = dcesrv_output_blob(p->dce_conn, &data);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -611,7 +611,7 @@ static NTSTATUS ipc_dcerpc_cmd(struct request_context *req, struct smb_trans2 *t
          async calls. Again, we only expect NT_STATUS_OK. If the call fails then
          the error is encoded at the dcerpc level
        */
-       status = dcesrv_output(p->dce_conn, &trans->out.data);
+       status = dcesrv_output_blob(p->dce_conn, &trans->out.data);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
index a4f5fb9768ec208172fb3cd9989518759f41b33e..0553537cb539f12467ca1139d7b9b8a3f8ba1090 100644 (file)
@@ -885,14 +885,23 @@ NTSTATUS dcesrv_input(struct dcesrv_connection *dce_conn, const DATA_BLOB *data)
 }
 
 /*
-  retrieve some output from a dcerpc server. The amount of data that
-  is wanted is in data->length and data->data is already allocated
-  to hold that much data.
+  retrieve some output from a dcerpc server
+  The caller supplies a function that will be called to do the
+  actual output. 
+
+  The first argument to write_fn() will be 'private', the second will
+  be a pointer to a buffer containing the data to be sent and the 3rd
+  will be the number of bytes to be sent.
+
+  write_fn() should return the number of bytes successfully written.
 */
-NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, DATA_BLOB *data)
+NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, 
+                      void *private,
+                      ssize_t (*write_fn)(void *, const void *, size_t))
 {
        struct dcesrv_call_state *call;
        struct dcesrv_call_reply *rep;
+       ssize_t nwritten;
 
        call = dce_conn->call_list;
        if (!call || !call->replies) {
@@ -900,13 +909,15 @@ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, DATA_BLOB *data)
        }
        rep = call->replies;
 
-       if (data->length >= rep->data.length) {
-               data->length = rep->data.length;
+       nwritten = write_fn(private, rep->data.data, rep->data.length);
+       if (nwritten == -1) {
+               /* TODO: hmm, how do we cope with this? destroy the
+                  connection perhaps? */
+               return NT_STATUS_UNSUCCESSFUL;
        }
 
-       memcpy(data->data, rep->data.data, data->length);
-       rep->data.length -= data->length;
-       rep->data.data += data->length;
+       rep->data.length -= nwritten;
+       rep->data.data += nwritten;
 
        if (rep->data.length == 0) {
                /* we're done with this section of the call */
@@ -922,6 +933,30 @@ NTSTATUS dcesrv_output(struct dcesrv_connection *dce_conn, DATA_BLOB *data)
        return NT_STATUS_OK;
 }
 
+
+/*
+  write_fn() for dcesrv_output_blob()
+*/
+static ssize_t dcesrv_output_blob_write_fn(void *private, const void *buf, size_t count)
+{
+       DATA_BLOB *blob = private;
+       if (count < blob->length) {
+               blob->length = count;
+       }
+       memcpy(blob->data, buf, blob->length);
+       return blob->length;
+}
+
+/*
+  a simple wrapper for dcesrv_output() for when we want to output
+  into a data blob
+*/
+NTSTATUS dcesrv_output_blob(struct dcesrv_connection *dce_conn, 
+                           DATA_BLOB *blob)
+{
+       return dcesrv_output(dce_conn, blob, dcesrv_output_blob_write_fn);
+}
+
 /*
   initialise the dcerpc server context
 */
index 34e6db63e35691f2444d9aecff3208c586493661..cc7581ee2fe3f6a3ed5ea735bf4d388f619e5ee5 100644 (file)
@@ -54,6 +54,21 @@ static void terminate_rpc_session(struct rpc_server_context *r, const char *reas
        r->model_ops->terminate_rpc_connection(r, reason);
 }
 
+
+/*
+  write_fn callback for dcesrv_output()
+*/
+static ssize_t dcerpc_write_fn(void *private, const void *buf, size_t count)
+{
+       struct fd_event *fde = private;
+       ssize_t ret;
+       ret = write(fde->fd, buf, count);
+       if (ret == -1 && errno == EINTR) {
+               return 0;
+       }
+       return ret;
+}
+
 /*
   called when a RPC socket becomes writable
 */
@@ -61,26 +76,16 @@ static void dcerpc_write_handler(struct event_context *ev, struct fd_event *fde,
                                 time_t t, uint16 flags)
 {
        struct rpc_server_context *r = fde->private;
-       DATA_BLOB blob;
        NTSTATUS status;
 
-       blob = data_blob(NULL, 0x4000);
-       if (!blob.data) {
-               terminate_rpc_session(r, "out of memory");
-               return;
-       }
-
-       status = dcesrv_output(r->dce_conn, &blob);
-
-       if (NT_STATUS_IS_OK(status)) {
-               write_data(fde->fd, blob.data, blob.length);
+       status = dcesrv_output(r->dce_conn, fde, dcerpc_write_fn);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* TODO: destroy fd_event? */
        }
 
        if (!r->dce_conn->call_list || !r->dce_conn->call_list->replies) {
                fde->flags &= ~EVENT_FD_WRITE;
        }
-
-       data_blob_free(&blob);
 }
 
 /*