From 7a4da9654e30ea96b326448c3e9111c2a5604f58 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Tue, 20 Jan 2004 05:54:17 +0000 Subject: 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) --- source4/rpc_server/dcerpc_server.c | 53 +++++++++++++++++++++++++++++++------- source4/rpc_server/dcerpc_tcp.c | 31 ++++++++++++---------- 2 files changed, 62 insertions(+), 22 deletions(-) (limited to 'source4/rpc_server') 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); } /* -- cgit