diff options
| author | Simo Sorce <idra@samba.org> | 2010-06-18 10:56:58 -0400 | 
|---|---|---|
| committer | Andreas Schneider <asn@samba.org> | 2010-09-15 12:53:42 +0200 | 
| commit | e7c4555b7caf379af38403fe497eea58415b51ed (patch) | |
| tree | 5b4c0009820215ac9378391b1f333cd82f5cbbe8 | |
| parent | f7f9916dc78d2fb31f65a5186ce68a66fd707cd7 (diff) | |
| download | samba-e7c4555b7caf379af38403fe497eea58415b51ed.tar.gz samba-e7c4555b7caf379af38403fe497eea58415b51ed.tar.bz2 samba-e7c4555b7caf379af38403fe497eea58415b51ed.zip  | |
s3-rpc_server: Accept connections and process requests.
Signed-off-by: Andreas Schneider <asn@cynapses.org>
| -rw-r--r-- | source3/rpc_server/rpc_server.c | 354 | 
1 files changed, 337 insertions, 17 deletions
diff --git a/source3/rpc_server/rpc_server.c b/source3/rpc_server/rpc_server.c index 89e224714c..7abb4598b4 100644 --- a/source3/rpc_server/rpc_server.c +++ b/source3/rpc_server/rpc_server.c @@ -23,6 +23,7 @@  #include "librpc/gen_ndr/netlogon.h"  #include "registry/reg_parse_prs.h"  #include "lib/tsocket/tsocket.h" +#include "libcli/named_pipe_auth/npa_tstream.h"  /* Creates a pipes_struct and initializes it with the information   * sent from the client */ @@ -60,24 +61,9 @@ static int make_server_pipes_struct(TALLOC_CTX *mem_ctx,  		return -1;  	} -	/* -	 * Initialize the incoming RPC data buffer with one PDU worth of -	 * memory. We cheat here and say we're marshalling, as we intend -	 * to add incoming data directly into the prs_struct and we want -	 * it to auto grow. We will change the type to UNMARSALLING before -	 * processing the stream. -	 */ -	if (!prs_init(&p->in_data.data, 128, p->mem_ctx, MARSHALL)) { -		DEBUG(0, ("malloc fail for in_data struct.\n")); -		TALLOC_FREE(p); -		*perrno = ENOMEM; -		return -1; -	} -	/* -	 * Initialize the outgoing RPC data buffer with no memory. -	 */ -	prs_init_empty(&p->out_data.rdata, p->mem_ctx, MARSHALL); +	data_blob_free(&p->in_data.data); +	data_blob_free(&p->in_data.pdu);  	p->endian = RPC_LITTLE_ENDIAN; @@ -304,7 +290,341 @@ static void named_pipe_listener(struct tevent_context *ev,  	named_pipe_accept_function(state->name, sd);  } + +/* This is the core of the rpc server. + * Accepts connections from clients and process requests using the appropriate + * dispatcher table. */ + +struct named_pipe_client { +	const char *pipe_name; +	struct ndr_syntax_id pipe_id; + +	struct tevent_context *ev; + +	uint16_t file_type; +	uint16_t device_state; +	uint64_t allocation_size; + +	struct tstream_context *tstream; + +	struct tsocket_address *client; +	char *client_name; +	struct tsocket_address *server; +	char *server_name; +	struct netr_SamInfo3 *info3; +	DATA_BLOB session_key; +	DATA_BLOB delegated_creds; + +	struct pipes_struct *p; + +	struct tevent_queue *write_queue; + +	struct iovec *iov; +	size_t count; +}; + +static void named_pipe_accept_done(struct tevent_req *subreq); +  static void named_pipe_accept_function(const char *pipe_name, int fd)  { +	struct ndr_syntax_id syntax; +	struct named_pipe_client *npc; +	struct tstream_context *plain; +	struct tevent_req *subreq; +	bool ok; +	int ret; + +	ok = is_known_pipename(pipe_name, &syntax); +	if (!ok) { +		DEBUG(1, ("Unknown pipe [%s]\n", pipe_name)); +		close(fd); +		return; +	} + +	npc = talloc_zero(NULL, struct named_pipe_client); +	if (!npc) { +		DEBUG(0, ("Out of memory!\n")); +		close(fd); +		return; +	} +	npc->pipe_name = pipe_name; +	npc->pipe_id = syntax; +	npc->ev = server_event_context(); + +	/* make sure socket is in NON blocking state */ +	ret = set_blocking(fd, false); +	if (ret != 0) { +		DEBUG(2, ("Failed to make socket non-blocking\n")); +		TALLOC_FREE(npc); +		close(fd); +		return; +	} + +	ret = tstream_bsd_existing_socket(npc, fd, &plain); +	if (ret != 0) { +		DEBUG(2, ("Failed to create tstream socket\n")); +		TALLOC_FREE(npc); +		close(fd); +		return; +	} + +	npc->file_type = FILE_TYPE_MESSAGE_MODE_PIPE; +	npc->device_state = 0xff | 0x0400 | 0x0100; +	npc->allocation_size = 4096; + +	subreq = tstream_npa_accept_existing_send(npc, npc->ev, plain, +						  npc->file_type, +						  npc->device_state, +						  npc->allocation_size); +	if (!subreq) { +		DEBUG(2, ("Failed to start async accept procedure\n")); +		TALLOC_FREE(npc); +		close(fd); +		return; +	} +	tevent_req_set_callback(subreq, named_pipe_accept_done, npc); +} + +static void named_pipe_packet_process(struct tevent_req *subreq); +static void named_pipe_packet_done(struct tevent_req *subreq); + +static void named_pipe_accept_done(struct tevent_req *subreq) +{ +	struct named_pipe_client *npc = +		tevent_req_callback_data(subreq, struct named_pipe_client); +	const char *cli_addr; +	int error; +	int ret; + +	ret = tstream_npa_accept_existing_recv(subreq, &error, npc, +						&npc->tstream, +						&npc->client, +						&npc->client_name, +						&npc->server, +						&npc->server_name, +						&npc->info3, +						&npc->session_key, +						&npc->delegated_creds); +	TALLOC_FREE(subreq); +	if (ret != 0) { +		DEBUG(2, ("Failed to accept named pipe connection! (%s)\n", +			  strerror(error))); +		TALLOC_FREE(npc); +		return; +	} + +	if (tsocket_address_is_inet(npc->client, "ip")) { +		cli_addr = tsocket_address_inet_addr_string(npc->client, +							    subreq); +		if (cli_addr == NULL) { +			TALLOC_FREE(npc); +			return; +		} +	} else { +		cli_addr = ""; +	} + +	ret = make_server_pipes_struct(npc, +					npc->pipe_name, npc->pipe_id, +					cli_addr, npc->info3, +					&npc->p, &error); +	if (ret != 0) { +		DEBUG(2, ("Failed to create pipes_struct! (%s)\n", +			  strerror(error))); +		goto fail; +	} + +	npc->write_queue = tevent_queue_create(npc, "np_server_write_queue"); +	if (!npc->write_queue) { +		DEBUG(2, ("Failed to set up write queue!\n")); +		goto fail; +	} + +	/* And now start receaving and processing packets */ +	subreq = named_pipe_read_packet_send(npc, npc->ev, npc->tstream); +	if (!subreq) { +		DEBUG(2, ("Failed to start receving packets\n")); +		goto fail; +	} +	tevent_req_set_callback(subreq, named_pipe_packet_process, npc); +	return; + +fail: +	DEBUG(2, ("Fatal error. Terminating client(%s) connection!\n", +		  npc->client_name)); +	/* terminate client connection */ +	talloc_free(npc); +	return; +} + +static void named_pipe_packet_process(struct tevent_req *subreq) +{ +	struct named_pipe_client *npc = +		tevent_req_callback_data(subreq, struct named_pipe_client); +	struct _output_data *out = &npc->p->out_data; +	DATA_BLOB recv_buffer; +	NTSTATUS status; +	ssize_t data_left; +	ssize_t data_used; +	char *data; +	uint32_t to_send; +	bool ok; + +	status = named_pipe_read_packet_recv(subreq, npc, &recv_buffer); +	TALLOC_FREE(subreq); +	if (!NT_STATUS_IS_OK(status)) { +		goto fail; +	} + +	data_left = recv_buffer.length; +	data = (char *)recv_buffer.data; + +	while (data_left) { + +		data_used = process_incoming_data(npc->p, data, data_left); +		if (data_used < 0) { +			DEBUG(3, ("Failed to process dceprc request!\n")); +			status = NT_STATUS_UNEXPECTED_IO_ERROR; +			goto fail; +		} + +		data_left -= data_used; +		data += data_used; +	} + +	/* Do not leak this buffer, npc is a long lived context */ +	talloc_free(recv_buffer.data); + +	/* this is needed because of the way DCERPC Binds work in +	 * the RPC marshalling code */ +	to_send = out->frag.length - out->current_pdu_sent; +	if (to_send > 0) { + +		DEBUG(10, ("Current_pdu_len = %u, " +			   "current_pdu_sent = %u " +			   "Returning %u bytes\n", +			   (unsigned int)out->frag.length, +			   (unsigned int)out->current_pdu_sent, +			   (unsigned int)to_send)); + +		npc->iov = talloc_zero(npc, struct iovec); +		if (!npc->iov) { +			status = NT_STATUS_NO_MEMORY; +			goto fail; +		} +		npc->count = 1; + +		npc->iov[0].iov_base = out->frag.data +					+ out->current_pdu_sent; +		npc->iov[0].iov_len = to_send; + +		out->current_pdu_sent += to_send; +	} + +	/* this condition is false for bind packets, or when we haven't +	 * yet got a full request, and need to wait for more data from +	 * the client */ +	while (out->data_sent_length < out->rdata.length) { + +		ok = create_next_pdu(npc->p); +		if (!ok) { +			DEBUG(3, ("Failed to create next PDU!\n")); +			status = NT_STATUS_UNEXPECTED_IO_ERROR; +			goto fail; +		} + +		npc->iov = talloc_realloc(npc, npc->iov, +					    struct iovec, npc->count + 1); +		if (!npc->iov) { +			status = NT_STATUS_NO_MEMORY; +			goto fail; +		} + +		npc->iov[npc->count].iov_base = out->frag.data; +		npc->iov[npc->count].iov_len = out->frag.length; + +		DEBUG(10, ("PDU number: %d, PDU Length: %u\n", +			   (unsigned int)npc->count, +			   (unsigned int)npc->iov[npc->count].iov_len)); +		dump_data(11, npc->iov[npc->count].iov_base, +				npc->iov[npc->count].iov_len); +		npc->count++; +	} + +	/* we still don't have a complete request, go back and wait for more +	 * data */ +	if (npc->count == 0) { +		/* Wait for the next packet */ +		subreq = named_pipe_read_packet_send(npc, npc->ev, npc->tstream); +		if (!subreq) { +			DEBUG(2, ("Failed to start receving packets\n")); +			status = NT_STATUS_NO_MEMORY; +			goto fail; +		} +		tevent_req_set_callback(subreq, named_pipe_packet_process, npc); +		return; +	} + +	DEBUG(10, ("Sending a total of %u bytes\n", +		   (unsigned int)npc->p->out_data.data_sent_length)); + +	subreq = tstream_writev_queue_send(npc, npc->ev, +					   npc->tstream, +					   npc->write_queue, +					   npc->iov, npc->count); +	if (!subreq) { +		DEBUG(2, ("Failed to send packet\n")); +		status = NT_STATUS_NO_MEMORY; +		goto fail; +	} +	tevent_req_set_callback(subreq, named_pipe_packet_done, npc); +	return; + +fail: +	DEBUG(2, ("Fatal error(%s). " +		  "Terminating client(%s) connection!\n", +		  nt_errstr(status), npc->client_name)); +	/* terminate client connection */ +	talloc_free(npc);  	return;  } + +static void named_pipe_packet_done(struct tevent_req *subreq) +{ +	struct named_pipe_client *npc = +		tevent_req_callback_data(subreq, struct named_pipe_client); +	int sys_errno; +	int ret; + +	ret = tstream_writev_queue_recv(subreq, &sys_errno); +	TALLOC_FREE(subreq); +	if (ret == -1) { +		DEBUG(2, ("Writev failed!\n")); +		goto fail; +	} + +	/* clear out any data that may have been left around */ +	npc->count = 0; +	TALLOC_FREE(npc->iov); +	data_blob_free(&npc->p->in_data.data); +	data_blob_free(&npc->p->out_data.frag); +	data_blob_free(&npc->p->out_data.rdata); + +	/* Wait for the next packet */ +	subreq = named_pipe_read_packet_send(npc, npc->ev, npc->tstream); +	if (!subreq) { +		DEBUG(2, ("Failed to start receving packets\n")); +		sys_errno = ENOMEM; +		goto fail; +	} +	tevent_req_set_callback(subreq, named_pipe_packet_process, npc); +	return; + +fail: +	DEBUG(2, ("Fatal error(%s). " +		  "Terminating client(%s) connection!\n", +		  strerror(sys_errno), npc->client_name)); +	/* terminate client connection */ +	talloc_free(npc); +	return; + }  | 
