summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2004-01-20 05:54:17 +0000
committerAndrew Tridgell <tridge@samba.org>2004-01-20 05:54:17 +0000
commit7a4da9654e30ea96b326448c3e9111c2a5604f58 (patch)
tree628dbc1eeaf7034349708149e2a49d3fb402f65a
parent4d39861f991254aa381b8823476825e26a4d6da3 (diff)
downloadsamba-7a4da9654e30ea96b326448c3e9111c2a5604f58.tar.gz
samba-7a4da9654e30ea96b326448c3e9111c2a5604f58.tar.bz2
samba-7a4da9654e30ea96b326448c3e9111c2a5604f58.zip
dcerpc server output now copes with the client blocking part way
through a read. This happens to also avoid a memcpy on output for dcerpc over tcp. (This used to be commit e7c53ad1856e299d82d84b5837189ae3191c32de)
-rw-r--r--source4/ntvfs/ipc/vfs_ipc.c4
-rw-r--r--source4/rpc_server/dcerpc_server.c53
-rw-r--r--source4/rpc_server/dcerpc_tcp.c31
3 files changed, 64 insertions, 24 deletions
diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c
index cd300b6589..c6d5a52960 100644
--- a/source4/ntvfs/ipc/vfs_ipc.c
+++ b/source4/ntvfs/ipc/vfs_ipc.c
@@ -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;
}
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
index a4f5fb9768..0553537cb5 100644
--- a/source4/rpc_server/dcerpc_server.c
+++ b/source4/rpc_server/dcerpc_server.c
@@ -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
*/
diff --git a/source4/rpc_server/dcerpc_tcp.c b/source4/rpc_server/dcerpc_tcp.c
index 34e6db63e3..cc7581ee2f 100644
--- a/source4/rpc_server/dcerpc_tcp.c
+++ b/source4/rpc_server/dcerpc_tcp.c
@@ -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);
}
/*