This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
return NT_STATUS_OK;
}
+static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status);
static void dcerpc_ship_next_request(struct dcerpc_connection *c);
/* destroy a dcerpc connection */
-static int dcerpc_connection_destructor(struct dcerpc_connection *c)
+static int dcerpc_connection_destructor(struct dcerpc_connection *conn)
{
- if (c->transport.shutdown_pipe) {
- c->transport.shutdown_pipe(c);
+ if (conn->dead) {
+ conn->free_skipped = True;
+ return -1;
}
+ dcerpc_connection_dead(conn, NT_STATUS_LOCAL_DISCONNECT);
return 0;
}
/* initialise a dcerpc connection.
the event context is optional
*/
-struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
+static struct dcerpc_connection *dcerpc_connection_init(TALLOC_CTX *mem_ctx,
struct event_context *ev)
{
struct dcerpc_connection *c;
p->last_fault_code = 0;
p->context_id = 0;
p->request_timeout = DCERPC_REQUEST_TIMEOUT;
+ p->binding = NULL;
ZERO_STRUCT(p->syntax);
ZERO_STRUCT(p->transfer_syntax);
return status;
}
-
/* check signature or unseal the packet */
switch (c->security_state.auth_info->auth_level) {
case DCERPC_AUTH_LEVEL_PRIVACY:
ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
}
- if (pkt->pfc_flags & DCERPC_PFC_FLAG_ORPC) {
+ if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT;
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- status = data_blob_realloc(mem_ctx, blob,
- blob->length - c->security_state.auth_info->credentials.length +
- creds2.length);
-
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- memcpy(blob->data + blob->length - c->security_state.auth_info->credentials.length,
- creds2.data, creds2.length);
-
+ blob->length -= c->security_state.auth_info->credentials.length;
+ status = data_blob_append(mem_ctx, blob,
+ creds2.data, creds2.length);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
dcerpc_set_auth_length(blob, creds2.length);
+ if (c->security_state.auth_info->credentials.length == 0) {
+ /* this is needed for krb5 only, to correct the total packet
+ length */
+ dcerpc_set_frag_length(blob,
+ dcerpc_get_frag_length(blob)
+ +creds2.length);
+ }
break;
case DCERPC_AUTH_LEVEL_INTEGRITY:
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- status = data_blob_realloc(mem_ctx, blob,
- blob->length - c->security_state.auth_info->credentials.length +
- creds2.length);
-
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
- memcpy(blob->data + blob->length - c->security_state.auth_info->credentials.length,
- creds2.data, creds2.length);
-
+ blob->length -= c->security_state.auth_info->credentials.length;
+ status = data_blob_append(mem_ctx, blob,
+ creds2.data, creds2.length);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
dcerpc_set_auth_length(blob, creds2.length);
+ if (c->security_state.auth_info->credentials.length == 0) {
+ /* this is needed for krb5 only, to correct the total packet
+ length */
+ dcerpc_set_frag_length(blob,
+ dcerpc_get_frag_length(blob)
+ +creds2.length);
+ }
break;
case DCERPC_AUTH_LEVEL_CONNECT:
*/
static void dcerpc_composite_fail(struct rpc_request *req)
{
- struct composite_context *c = talloc_get_type(req->async.private,
+ struct composite_context *c = talloc_get_type(req->async.private_data,
struct composite_context);
composite_error(c, req->status);
}
+/*
+ remove requests from the pending or queued queues
+ */
+static int dcerpc_req_dequeue(struct rpc_request *req)
+{
+ switch (req->state) {
+ case RPC_REQUEST_QUEUED:
+ DLIST_REMOVE(req->p->conn->request_queue, req);
+ break;
+ case RPC_REQUEST_PENDING:
+ DLIST_REMOVE(req->p->conn->pending, req);
+ break;
+ case RPC_REQUEST_DONE:
+ break;
+ }
+ return 0;
+}
+
+
/*
mark the dcerpc connection dead. All outstanding requests get an error
*/
static void dcerpc_connection_dead(struct dcerpc_connection *conn, NTSTATUS status)
{
+ if (conn->dead) return;
+
+ conn->dead = true;
+
+ if (conn->transport.shutdown_pipe) {
+ conn->transport.shutdown_pipe(conn, status);
+ }
+
/* all pending requests get the error */
while (conn->pending) {
struct rpc_request *req = conn->pending;
+ dcerpc_req_dequeue(req);
req->state = RPC_REQUEST_DONE;
req->status = status;
- DLIST_REMOVE(conn->pending, req);
if (req->async.callback) {
req->async.callback(req);
}
}
+
+ talloc_set_destructor(conn, NULL);
+ if (conn->free_skipped) {
+ talloc_free(conn);
+ }
}
/*
struct composite_context *c;
struct dcerpc_connection *conn;
- c = talloc_get_type(req->async.private, struct composite_context);
+ c = talloc_get_type(req->async.private_data, struct composite_context);
if (pkt->ptype == DCERPC_PKT_BIND_NAK) {
DEBUG(2,("dcerpc: bind_nak reason %d\n",
if (!composite_is_ok(c)) return;
}
+ req->p->assoc_group_id = pkt->u.bind_ack.assoc_group_id;
+
composite_done(c);
}
{
struct rpc_request *req = talloc_get_type(private, struct rpc_request);
- if (req->state != RPC_REQUEST_PENDING) {
+ if (req->ignore_timeout) {
+ dcerpc_req_dequeue(req);
+ req->state = RPC_REQUEST_DONE;
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->async.callback) {
+ req->async.callback(req);
+ }
return;
}
- req->status = NT_STATUS_IO_TIMEOUT;
- req->state = RPC_REQUEST_DONE;
- DLIST_REMOVE(req->p->conn->pending, req);
- if (req->async.callback) {
- req->async.callback(req);
- }
+ dcerpc_connection_dead(req->p->conn, NT_STATUS_IO_TIMEOUT);
}
-
/*
send a async dcerpc bind request
*/
pkt.call_id = p->conn->call_id;
pkt.auth_length = 0;
+ if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
pkt.u.bind.max_xmit_frag = 5840;
pkt.u.bind.max_recv_frag = 5840;
- pkt.u.bind.assoc_group_id = 0;
+ pkt.u.bind.assoc_group_id = p->binding->assoc_group_id;
pkt.u.bind.num_contexts = 1;
pkt.u.bind.ctx_list = talloc_array(mem_ctx, struct dcerpc_ctx_list, 1);
if (composite_nomem(pkt.u.bind.ctx_list, c)) return c;
req->state = RPC_REQUEST_PENDING;
req->call_id = pkt.call_id;
- req->async.private = c;
+ req->async.private_data = c;
req->async.callback = dcerpc_composite_fail;
req->p = p;
req->recv_handler = dcerpc_bind_recv_handler;
DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
c->status = p->conn->transport.send_request(p->conn, &blob,
True);
return result;
}
-/*
- perform a bind using the given syntax
-
- the auth_info structure is updated with the reply authentication info
- on success
-*/
-NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
- TALLOC_CTX *mem_ctx,
- const struct dcerpc_syntax_id *syntax,
- const struct dcerpc_syntax_id *transfer_syntax)
-{
- struct composite_context *creq;
- creq = dcerpc_bind_send(p, mem_ctx, syntax, transfer_syntax);
- return dcerpc_bind_recv(creq);
-}
-
/*
perform a continued bind (and auth3)
*/
}
-/*
- return the rpc syntax and transfer syntax given the pipe uuid and version
-*/
-NTSTATUS dcerpc_init_syntaxes(const struct dcerpc_interface_table *table,
- struct dcerpc_syntax_id *syntax,
- struct dcerpc_syntax_id *transfer_syntax)
-{
- syntax->uuid = table->syntax_id.uuid;
- syntax->if_version = table->syntax_id.if_version;
-
- *transfer_syntax = ndr_transfer_syntax;
-
- return NT_STATUS_OK;
-}
-
-/* perform a dcerpc bind, using the uuid as the key */
-NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p,
- TALLOC_CTX *mem_ctx,
- const struct dcerpc_interface_table *table)
-{
- struct dcerpc_syntax_id syntax;
- struct dcerpc_syntax_id transfer_syntax;
- NTSTATUS status;
-
- status = dcerpc_init_syntaxes(table,
- &syntax, &transfer_syntax);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
- return status;
- }
-
- return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
-}
-
-
/*
process a fragment received from the transport layer during a
request
talloc_steal(req, raw_packet->data);
if (req->recv_handler != NULL) {
+ dcerpc_req_dequeue(req);
req->state = RPC_REQUEST_DONE;
- DLIST_REMOVE(c->pending, req);
req->recv_handler(req, raw_packet, pkt);
return;
}
}
}
-/*
- make sure requests are cleaned up
- */
-static int dcerpc_req_destructor(struct rpc_request *req)
-{
- DLIST_REMOVE(req->p->conn->pending, req);
- return 0;
-}
-
/*
perform the send side of a async dcerpc request
*/
req->p = p;
req->call_id = next_call_id(p->conn);
req->status = NT_STATUS_OK;
- req->state = RPC_REQUEST_PENDING;
+ req->state = RPC_REQUEST_QUEUED;
req->payload = data_blob(NULL, 0);
req->flags = 0;
req->fault_code = 0;
req->async_call = async;
+ req->ignore_timeout = False;
req->async.callback = NULL;
- req->async.private = NULL;
+ req->async.private_data = NULL;
req->recv_handler = NULL;
if (object != NULL) {
req->opnum = opnum;
req->request_data.length = stub_data->length;
req->request_data.data = talloc_reference(req, stub_data->data);
- if (req->request_data.data == NULL) {
+ if (req->request_data.length && req->request_data.data == NULL) {
return NULL;
}
DLIST_ADD_END(p->conn->request_queue, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
dcerpc_ship_next_request(p->conn);
dcerpc_timeout_handler, req);
}
- talloc_set_destructor(req, dcerpc_req_destructor);
return req;
}
DLIST_REMOVE(c->request_queue, req);
DLIST_ADD(c->pending, req);
+ req->state = RPC_REQUEST_PENDING;
init_ncacn_hdr(p->conn, &pkt);
if (req->object) {
pkt.u.request.object.object = *req->object;
- pkt.pfc_flags |= DCERPC_PFC_FLAG_ORPC;
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_OBJECT_UUID;
chunk_size -= ndr_size_GUID(req->object,0);
}
{
NTSTATUS status;
- while (req->state == RPC_REQUEST_PENDING) {
+ while (req->state != RPC_REQUEST_DONE) {
struct event_context *ctx = dcerpc_event_context(req->p);
if (event_loop_once(ctx) != 0) {
return NT_STATUS_CONNECTION_DISCONNECTED;
blob2 = ndr_push_blob(push);
- if (!data_blob_equal(&blob, &blob2)) {
+ if (data_blob_cmp(&blob, &blob2) != 0) {
DEBUG(3,("original:\n"));
dump_data(3, blob.data, blob.length);
DEBUG(3,("secondary:\n"));
blob2 = ndr_push_blob(push);
- if (!data_blob_equal(&blob, &blob2)) {
+ if (data_blob_cmp(&blob, &blob2) != 0) {
DEBUG(3,("original:\n"));
dump_data(3, blob.data, blob.length);
DEBUG(3,("secondary:\n"));
/*
receive the answer from a dcerpc_ndr_request_send()
*/
-NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
+_PUBLIC_ NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req)
{
struct dcerpc_pipe *p = req->p;
NTSTATUS status;
/* make sure the recv code doesn't free the request, as we
need to grab the flags element before it is freed */
- talloc_increase_ref_count(req);
+ if (talloc_reference(p, req) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
status = dcerpc_request_recv(req, mem_ctx, &response);
if (!NT_STATUS_IS_OK(status)) {
+ talloc_unlink(p, req);
return status;
}
/* prepare for ndr_pull_* */
pull = ndr_pull_init_flags(p->conn, &response, mem_ctx);
if (!pull) {
- talloc_free(req);
+ talloc_unlink(p, req);
return NT_STATUS_NO_MEMORY;
}
if (pull->data) {
pull->data = talloc_steal(pull, pull->data);
}
- talloc_free(req);
+ talloc_unlink(p, req);
if (flags & DCERPC_PULL_BIGENDIAN) {
pull->flags |= LIBNDR_FLAG_BIGENDIAN;
struct composite_context *c;
struct dcerpc_pipe *recv_pipe;
- c = talloc_get_type(req->async.private, struct composite_context);
+ c = talloc_get_type(req->async.private_data, struct composite_context);
recv_pipe = talloc_get_type(c->private_data, struct dcerpc_pipe);
if (pkt->ptype == DCERPC_PKT_ALTER_RESP &&
pkt.call_id = p->conn->call_id;
pkt.auth_length = 0;
+ if (p->binding->flags & DCERPC_CONCURRENT_MULTIPLEX) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_CONC_MPX;
+ }
+
pkt.u.alter.max_xmit_frag = 5840;
pkt.u.alter.max_recv_frag = 5840;
- pkt.u.alter.assoc_group_id = 0;
+ pkt.u.alter.assoc_group_id = p->binding->assoc_group_id;
pkt.u.alter.num_contexts = 1;
pkt.u.alter.ctx_list = talloc_array(c, struct dcerpc_ctx_list, 1);
if (composite_nomem(pkt.u.alter.ctx_list, c)) return c;
req->state = RPC_REQUEST_PENDING;
req->call_id = pkt.call_id;
- req->async.private = c;
+ req->async.private_data = c;
req->async.callback = dcerpc_composite_fail;
req->p = p;
req->recv_handler = dcerpc_alter_recv_handler;
DLIST_ADD_END(p->conn->pending, req, struct rpc_request *);
+ talloc_set_destructor(req, dcerpc_req_dequeue);
c->status = p->conn->transport.send_request(p->conn, &blob, True);
if (!composite_is_ok(c)) return c;