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;
}
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);
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;