diff options
Diffstat (limited to 'source4/librpc/rpc/binding.c')
| -rw-r--r-- | source4/librpc/rpc/binding.c | 724 | 
1 files changed, 724 insertions, 0 deletions
diff --git a/source4/librpc/rpc/binding.c b/source4/librpc/rpc/binding.c new file mode 100644 index 0000000000..ae88dce1be --- /dev/null +++ b/source4/librpc/rpc/binding.c @@ -0,0 +1,724 @@ +/*  +   Unix SMB/CIFS implementation. + +   dcerpc utility functions + +   Copyright (C) Andrew Tridgell 2003 +   Copyright (C) Jelmer Vernooij 2004 +   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005 +   Copyright (C) Rafal Szczesniak 2006 +    +   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 3 of the License, or +   (at your option) any later version. +    +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   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, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "librpc/gen_ndr/ndr_epmapper_c.h" +#include "librpc/gen_ndr/ndr_dcerpc.h" +#include "librpc/gen_ndr/ndr_misc.h" + +#define MAX_PROTSEQ		10 + +static const struct { +	const char *name; +	enum dcerpc_transport_t transport; +	int num_protocols; +	enum epm_protocol protseq[MAX_PROTSEQ]; +} transports[] = { +	{ "ncacn_np",     NCACN_NP, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_SMB, EPM_PROTOCOL_NETBIOS }}, +	{ "ncacn_ip_tcp", NCACN_IP_TCP, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_TCP, EPM_PROTOCOL_IP } },  +	{ "ncacn_http", NCACN_HTTP, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_HTTP, EPM_PROTOCOL_IP } },  +	{ "ncadg_ip_udp", NCACN_IP_UDP, 3,  +		{ EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UDP, EPM_PROTOCOL_IP } }, +	{ "ncalrpc", NCALRPC, 2,  +		{ EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_PIPE } }, +	{ "ncacn_unix_stream", NCACN_UNIX_STREAM, 2,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_UNIX_DS } }, +	{ "ncadg_unix_dgram", NCADG_UNIX_DGRAM, 2,  +		{ EPM_PROTOCOL_NCADG, EPM_PROTOCOL_UNIX_DS } }, +	{ "ncacn_at_dsp", NCACN_AT_DSP, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DSP } }, +	{ "ncadg_at_ddp", NCADG_AT_DDP, 3,  +		{ EPM_PROTOCOL_NCADG, EPM_PROTOCOL_APPLETALK, EPM_PROTOCOL_DDP } }, +	{ "ncacn_vns_ssp", NCACN_VNS_SPP, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_SPP } }, +	{ "ncacn_vns_ipc", NCACN_VNS_IPC, 3,  +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_STREETTALK, EPM_PROTOCOL_VINES_IPC }, }, +	{ "ncadg_ipx", NCADG_IPX, 2, +		{ EPM_PROTOCOL_NCADG, EPM_PROTOCOL_IPX }, +	}, +	{ "ncacn_spx", NCACN_SPX, 3, +		/* I guess some MS programmer confused the identifier for  +		 * EPM_PROTOCOL_UUID (0x0D or 13) with the one for  +		 * EPM_PROTOCOL_SPX (0x13) here. -- jelmer*/ +		{ EPM_PROTOCOL_NCACN, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID }, +	}, +}; + +static const struct { +	const char *name; +	uint32_t flag; +} ncacn_options[] = { +	{"sign", DCERPC_SIGN}, +	{"seal", DCERPC_SEAL}, +	{"connect", DCERPC_CONNECT}, +	{"spnego", DCERPC_AUTH_SPNEGO}, +	{"ntlm", DCERPC_AUTH_NTLM}, +	{"krb5", DCERPC_AUTH_KRB5}, +	{"validate", DCERPC_DEBUG_VALIDATE_BOTH}, +	{"print", DCERPC_DEBUG_PRINT_BOTH}, +	{"padcheck", DCERPC_DEBUG_PAD_CHECK}, +	{"bigendian", DCERPC_PUSH_BIGENDIAN}, +	{"smb2", DCERPC_SMB2} +}; + +const char *epm_floor_string(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor) +{ +	struct ndr_syntax_id syntax; +	NTSTATUS status; + +	switch(epm_floor->lhs.protocol) { +		case EPM_PROTOCOL_UUID: +			status = dcerpc_floor_get_lhs_data(epm_floor, &syntax); +			if (NT_STATUS_IS_OK(status)) { +				/* lhs is used: UUID */ +				char *uuidstr; + +				if (GUID_equal(&syntax.uuid, &ndr_transfer_syntax.uuid)) { +					return "NDR"; +				}  + +				if (GUID_equal(&syntax.uuid, &ndr64_transfer_syntax.uuid)) { +					return "NDR64"; +				}  + +				uuidstr = GUID_string(mem_ctx, &syntax.uuid); + +				return talloc_asprintf(mem_ctx, " uuid %s/0x%02x", uuidstr, syntax.if_version); +			} else { /* IPX */ +				return talloc_asprintf(mem_ctx, "IPX:%s",  +						data_blob_hex_string(mem_ctx, &epm_floor->rhs.uuid.unknown)); +			} + +		case EPM_PROTOCOL_NCACN: +			return "RPC-C"; + +		case EPM_PROTOCOL_NCADG: +			return "RPC"; + +		case EPM_PROTOCOL_NCALRPC: +			return "NCALRPC"; + +		case EPM_PROTOCOL_DNET_NSP: +			return "DNET/NSP"; + +		case EPM_PROTOCOL_IP: +			return talloc_asprintf(mem_ctx, "IP:%s", epm_floor->rhs.ip.ipaddr); + +		case EPM_PROTOCOL_PIPE: +			return talloc_asprintf(mem_ctx, "PIPE:%s", epm_floor->rhs.pipe.path); + +		case EPM_PROTOCOL_SMB: +			return talloc_asprintf(mem_ctx, "SMB:%s", epm_floor->rhs.smb.unc); + +		case EPM_PROTOCOL_UNIX_DS: +			return talloc_asprintf(mem_ctx, "Unix:%s", epm_floor->rhs.unix_ds.path); + +		case EPM_PROTOCOL_NETBIOS: +			return talloc_asprintf(mem_ctx, "NetBIOS:%s", epm_floor->rhs.netbios.name); + +		case EPM_PROTOCOL_NETBEUI: +			return "NETBeui"; + +		case EPM_PROTOCOL_SPX: +			return "SPX"; + +		case EPM_PROTOCOL_NB_IPX: +			return "NB_IPX"; + +		case EPM_PROTOCOL_HTTP: +			return talloc_asprintf(mem_ctx, "HTTP:%d", epm_floor->rhs.http.port); + +		case EPM_PROTOCOL_TCP: +			return talloc_asprintf(mem_ctx, "TCP:%d", epm_floor->rhs.tcp.port); + +		case EPM_PROTOCOL_UDP: +			return talloc_asprintf(mem_ctx, "UDP:%d", epm_floor->rhs.udp.port); + +		default: +			return talloc_asprintf(mem_ctx, "UNK(%02x):", epm_floor->lhs.protocol); +	} +} + + +/* +  form a binding string from a binding structure +*/ +_PUBLIC_ char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b) +{ +	char *s = talloc_strdup(mem_ctx, ""); +	int i; +	const char *t_name = NULL; + +	if (b->transport != NCA_UNKNOWN) { +		for (i=0;i<ARRAY_SIZE(transports);i++) { +			if (transports[i].transport == b->transport) { +				t_name = transports[i].name; +			} +		} +		if (!t_name) { +			return NULL; +		} +	} + +	if (!GUID_all_zero(&b->object.uuid)) {  +		s = talloc_asprintf(s, "%s@", +				    GUID_string(mem_ctx, &b->object.uuid)); +	} + +	if (t_name != NULL) { +		s = talloc_asprintf_append_buffer(s, "%s:", t_name); +		if (s == NULL) { +			return NULL; +		} +	} + +	if (b->host) { +		s = talloc_asprintf_append_buffer(s, "%s", b->host); +	} + +	if (!b->endpoint && !b->options && !b->flags) { +		return s; +	} + +	s = talloc_asprintf_append_buffer(s, "["); + +	if (b->endpoint) { +		s = talloc_asprintf_append_buffer(s, "%s", b->endpoint); +	} + +	/* this is a *really* inefficent way of dealing with strings, +	   but this is rarely called and the strings are always short, +	   so I don't care */ +	for (i=0;b->options && b->options[i];i++) { +		s = talloc_asprintf_append_buffer(s, ",%s", b->options[i]); +		if (!s) return NULL; +	} + +	for (i=0;i<ARRAY_SIZE(ncacn_options);i++) { +		if (b->flags & ncacn_options[i].flag) { +			s = talloc_asprintf_append_buffer(s, ",%s", ncacn_options[i].name); +			if (!s) return NULL; +		} +	} + +	s = talloc_asprintf_append_buffer(s, "]"); + +	return s; +} + +/* +  parse a binding string into a dcerpc_binding structure +*/ +_PUBLIC_ NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding **b_out) +{ +	struct dcerpc_binding *b; +	char *options; +	char *p; +	int i, j, comma_count; + +	b = talloc(mem_ctx, struct dcerpc_binding); +	if (!b) { +		return NT_STATUS_NO_MEMORY; +	} + +	p = strchr(s, '@'); + +	if (p && PTR_DIFF(p, s) == 36) { /* 36 is the length of a UUID */ +		NTSTATUS status; + +		status = GUID_from_string(s, &b->object.uuid); + +		if (NT_STATUS_IS_ERR(status)) { +			DEBUG(0, ("Failed parsing UUID\n")); +			return status; +		} + +		s = p + 1; +	} else { +		ZERO_STRUCT(b->object); +	} + +	b->object.if_version = 0; + +	p = strchr(s, ':'); + +	if (p == NULL) { +		b->transport = NCA_UNKNOWN; +	} else { +		char *type = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s)); +		if (!type) { +			return NT_STATUS_NO_MEMORY; +		} + +		for (i=0;i<ARRAY_SIZE(transports);i++) { +			if (strcasecmp(type, transports[i].name) == 0) { +				b->transport = transports[i].transport; +				break; +			} +		} + +		if (i==ARRAY_SIZE(transports)) { +			DEBUG(0,("Unknown dcerpc transport '%s'\n", type)); +			return NT_STATUS_INVALID_PARAMETER; +		} + +		talloc_free(type); +	 +		s = p+1; +	} + +	p = strchr(s, '['); +	if (p) { +		b->host = talloc_strndup(b, s, PTR_DIFF(p, s)); +		options = talloc_strdup(mem_ctx, p+1); +		if (options[strlen(options)-1] != ']') { +			return NT_STATUS_INVALID_PARAMETER; +		} +		options[strlen(options)-1] = 0; +	} else { +		b->host = talloc_strdup(b, s); +		options = NULL; +	} +	if (!b->host) { +		return NT_STATUS_NO_MEMORY; +	} + +	b->target_hostname = b->host; + +	b->options = NULL; +	b->flags = 0; +	b->assoc_group_id = 0; +	b->endpoint = NULL; + +	if (!options) { +		*b_out = b; +		return NT_STATUS_OK; +	} + +	comma_count = count_chars(options, ','); + +	b->options = talloc_array(b, const char *, comma_count+2); +	if (!b->options) { +		return NT_STATUS_NO_MEMORY; +	} + +	for (i=0; (p = strchr(options, ',')); i++) { +		b->options[i] = talloc_strndup(b, options, PTR_DIFF(p, options)); +		if (!b->options[i]) { +			return NT_STATUS_NO_MEMORY; +		} +		options = p+1; +	} +	b->options[i] = options; +	b->options[i+1] = NULL; + +	/* some options are pre-parsed for convenience */ +	for (i=0;b->options[i];i++) { +		for (j=0;j<ARRAY_SIZE(ncacn_options);j++) { +			if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) { +				int k; +				b->flags |= ncacn_options[j].flag; +				for (k=i;b->options[k];k++) { +					b->options[k] = b->options[k+1]; +				} +				i--; +				break; +			} +		} +	} + +	if (b->options[0]) { +		/* Endpoint is first option */ +		b->endpoint = b->options[0]; +		if (strlen(b->endpoint) == 0) b->endpoint = NULL; + +		for (i=0;b->options[i];i++) { +			b->options[i] = b->options[i+1]; +		} +	} + +	if (b->options[0] == NULL) +		b->options = NULL; +	 +	*b_out = b; +	return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS dcerpc_floor_get_lhs_data(struct epm_floor *epm_floor, struct ndr_syntax_id *syntax) +{ +	TALLOC_CTX *mem_ctx = talloc_init("floor_get_lhs_data"); +	struct ndr_pull *ndr = ndr_pull_init_blob(&epm_floor->lhs.lhs_data, mem_ctx, NULL); +	enum ndr_err_code ndr_err; +	uint16_t if_version=0; + +	ndr->flags |= LIBNDR_FLAG_NOALIGN; + +	ndr_err = ndr_pull_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid); +	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { +		talloc_free(mem_ctx); +		return ndr_map_error2ntstatus(ndr_err); +	} + +	ndr_err = ndr_pull_uint16(ndr, NDR_SCALARS, &if_version); +	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { +		talloc_free(mem_ctx); +		return ndr_map_error2ntstatus(ndr_err); +	} + +	syntax->if_version = if_version; + +	talloc_free(mem_ctx); + +	return NT_STATUS_OK; +} + +static DATA_BLOB dcerpc_floor_pack_lhs_data(TALLOC_CTX *mem_ctx, const struct ndr_syntax_id *syntax) +{ +	struct ndr_push *ndr = ndr_push_init_ctx(mem_ctx, NULL); + +	ndr->flags |= LIBNDR_FLAG_NOALIGN; + +	ndr_push_GUID(ndr, NDR_SCALARS | NDR_BUFFERS, &syntax->uuid); +	ndr_push_uint16(ndr, NDR_SCALARS, syntax->if_version); + +	return ndr_push_blob(ndr); +} + +const char *dcerpc_floor_get_rhs_data(TALLOC_CTX *mem_ctx, struct epm_floor *epm_floor) +{ +	switch (epm_floor->lhs.protocol) { +	case EPM_PROTOCOL_TCP: +		if (epm_floor->rhs.tcp.port == 0) return NULL; +		return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.tcp.port); +		 +	case EPM_PROTOCOL_UDP: +		if (epm_floor->rhs.udp.port == 0) return NULL; +		return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.udp.port); + +	case EPM_PROTOCOL_HTTP: +		if (epm_floor->rhs.http.port == 0) return NULL; +		return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.http.port); + +	case EPM_PROTOCOL_IP: +		return talloc_strdup(mem_ctx, epm_floor->rhs.ip.ipaddr); + +	case EPM_PROTOCOL_NCACN: +		return NULL; + +	case EPM_PROTOCOL_NCADG: +		return NULL; + +	case EPM_PROTOCOL_SMB: +		if (strlen(epm_floor->rhs.smb.unc) == 0) return NULL; +		return talloc_strdup(mem_ctx, epm_floor->rhs.smb.unc); + +	case EPM_PROTOCOL_PIPE: +		if (strlen(epm_floor->rhs.pipe.path) == 0) return NULL; +		return talloc_strdup(mem_ctx, epm_floor->rhs.pipe.path); + +	case EPM_PROTOCOL_NETBIOS: +		if (strlen(epm_floor->rhs.netbios.name) == 0) return NULL; +		return talloc_strdup(mem_ctx, epm_floor->rhs.netbios.name); + +	case EPM_PROTOCOL_NCALRPC: +		return NULL; +		 +	case EPM_PROTOCOL_VINES_SPP: +		return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_spp.port); +		 +	case EPM_PROTOCOL_VINES_IPC: +		return talloc_asprintf(mem_ctx, "%d", epm_floor->rhs.vines_ipc.port); +		 +	case EPM_PROTOCOL_STREETTALK: +		return talloc_strdup(mem_ctx, epm_floor->rhs.streettalk.streettalk); +		 +	case EPM_PROTOCOL_UNIX_DS: +		if (strlen(epm_floor->rhs.unix_ds.path) == 0) return NULL; +		return talloc_strdup(mem_ctx, epm_floor->rhs.unix_ds.path); +		 +	case EPM_PROTOCOL_NULL: +		return NULL; + +	default: +		DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol)); +		break; +	} + +	return NULL; +} + +static NTSTATUS dcerpc_floor_set_rhs_data(TALLOC_CTX *mem_ctx,  +					  struct epm_floor *epm_floor,   +					  const char *data) +{ +	switch (epm_floor->lhs.protocol) { +	case EPM_PROTOCOL_TCP: +		epm_floor->rhs.tcp.port = atoi(data); +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_UDP: +		epm_floor->rhs.udp.port = atoi(data); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_HTTP: +		epm_floor->rhs.http.port = atoi(data); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_IP: +		epm_floor->rhs.ip.ipaddr = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.ip.ipaddr); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_NCACN: +		epm_floor->rhs.ncacn.minor_version = 0; +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_NCADG: +		epm_floor->rhs.ncadg.minor_version = 0; +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_SMB: +		epm_floor->rhs.smb.unc = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.smb.unc); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_PIPE: +		epm_floor->rhs.pipe.path = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.pipe.path); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_NETBIOS: +		epm_floor->rhs.netbios.name = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.netbios.name); +		return NT_STATUS_OK; + +	case EPM_PROTOCOL_NCALRPC: +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_VINES_SPP: +		epm_floor->rhs.vines_spp.port = atoi(data); +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_VINES_IPC: +		epm_floor->rhs.vines_ipc.port = atoi(data); +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_STREETTALK: +		epm_floor->rhs.streettalk.streettalk = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.streettalk.streettalk); +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_UNIX_DS: +		epm_floor->rhs.unix_ds.path = talloc_strdup(mem_ctx, data); +		NT_STATUS_HAVE_NO_MEMORY(epm_floor->rhs.unix_ds.path); +		return NT_STATUS_OK; +		 +	case EPM_PROTOCOL_NULL: +		return NT_STATUS_OK; + +	default: +		DEBUG(0,("Unsupported lhs protocol %d\n", epm_floor->lhs.protocol)); +		break; +	} + +	return NT_STATUS_NOT_SUPPORTED; +} + +enum dcerpc_transport_t dcerpc_transport_by_endpoint_protocol(int prot) +{ +	int i; + +	/* Find a transport that has 'prot' as 4th protocol */ +	for (i=0;i<ARRAY_SIZE(transports);i++) { +		if (transports[i].num_protocols >= 2 &&  +			transports[i].protseq[1] == prot) { +			return transports[i].transport; +		} +	} +	 +	/* Unknown transport */ +	return (unsigned int)-1; +} + +_PUBLIC_ enum dcerpc_transport_t dcerpc_transport_by_tower(struct epm_tower *tower) +{ +	int i; + +	/* Find a transport that matches this tower */ +	for (i=0;i<ARRAY_SIZE(transports);i++) { +		int j; +		if (transports[i].num_protocols != tower->num_floors - 2) { +			continue;  +		} + +		for (j = 0; j < transports[i].num_protocols; j++) { +			if (transports[i].protseq[j] != tower->floors[j+2].lhs.protocol) { +				break; +			} +		} + +		if (j == transports[i].num_protocols) { +			return transports[i].transport; +		} +	} +	 +	/* Unknown transport */ +	return (unsigned int)-1; +} + +_PUBLIC_ NTSTATUS dcerpc_binding_from_tower(TALLOC_CTX *mem_ctx,  +				   struct epm_tower *tower,  +				   struct dcerpc_binding **b_out) +{ +	NTSTATUS status; +	struct dcerpc_binding *binding; + +	binding = talloc(mem_ctx, struct dcerpc_binding); +	NT_STATUS_HAVE_NO_MEMORY(binding); + +	ZERO_STRUCT(binding->object); +	binding->options = NULL; +	binding->host = NULL; +	binding->target_hostname = NULL; +	binding->flags = 0; +	binding->assoc_group_id = 0; + +	binding->transport = dcerpc_transport_by_tower(tower); + +	if (binding->transport == (unsigned int)-1) { +		return NT_STATUS_NOT_SUPPORTED; +	} + +	if (tower->num_floors < 1) { +		return NT_STATUS_OK; +	} + +	/* Set object uuid */ +	status = dcerpc_floor_get_lhs_data(&tower->floors[0], &binding->object); +	 +	if (!NT_STATUS_IS_OK(status)) { +		DEBUG(1, ("Error pulling object uuid and version: %s", nt_errstr(status)));	 +		return status; +	} + +	/* Ignore floor 1, it contains the NDR version info */ +	 +	binding->options = NULL; + +	/* Set endpoint */ +	if (tower->num_floors >= 4) { +		binding->endpoint = dcerpc_floor_get_rhs_data(mem_ctx, &tower->floors[3]); +	} else { +		binding->endpoint = NULL; +	} + +	/* Set network address */ +	if (tower->num_floors >= 5) { +		binding->host = dcerpc_floor_get_rhs_data(mem_ctx, &tower->floors[4]); +		NT_STATUS_HAVE_NO_MEMORY(binding->host); +		binding->target_hostname = binding->host; +	} +	*b_out = binding; +	return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS dcerpc_binding_build_tower(TALLOC_CTX *mem_ctx, struct dcerpc_binding *binding, struct epm_tower *tower) +{ +	const enum epm_protocol *protseq = NULL; +	int num_protocols = -1, i; +	NTSTATUS status; +	 +	/* Find transport */ +	for (i=0;i<ARRAY_SIZE(transports);i++) { +		if (transports[i].transport == binding->transport) { +			protseq = transports[i].protseq; +			num_protocols = transports[i].num_protocols; +			break; +		} +	} + +	if (num_protocols == -1) { +		DEBUG(0, ("Unable to find transport with id '%d'\n", binding->transport)); +		return NT_STATUS_UNSUCCESSFUL; +	} + +	tower->num_floors = 2 + num_protocols; +	tower->floors = talloc_array(mem_ctx, struct epm_floor, tower->num_floors); + +	/* Floor 0 */ +	tower->floors[0].lhs.protocol = EPM_PROTOCOL_UUID; + +	tower->floors[0].lhs.lhs_data = dcerpc_floor_pack_lhs_data(mem_ctx, &binding->object); + +	tower->floors[0].rhs.uuid.unknown = data_blob_talloc_zero(mem_ctx, 2); +	 +	/* Floor 1 */ +	tower->floors[1].lhs.protocol = EPM_PROTOCOL_UUID; + +	tower->floors[1].lhs.lhs_data = dcerpc_floor_pack_lhs_data(mem_ctx,  +								&ndr_transfer_syntax); +	 +	tower->floors[1].rhs.uuid.unknown = data_blob_talloc_zero(mem_ctx, 2); +	 +	/* Floor 2 to num_protocols */ +	for (i = 0; i < num_protocols; i++) { +		tower->floors[2 + i].lhs.protocol = protseq[i]; +		tower->floors[2 + i].lhs.lhs_data = data_blob_talloc(mem_ctx, NULL, 0); +		ZERO_STRUCT(tower->floors[2 + i].rhs); +		dcerpc_floor_set_rhs_data(mem_ctx, &tower->floors[2 + i], ""); +	} + +	/* The 4th floor contains the endpoint */ +	if (num_protocols >= 2 && binding->endpoint) { +		status = dcerpc_floor_set_rhs_data(mem_ctx, &tower->floors[3], binding->endpoint); +		if (NT_STATUS_IS_ERR(status)) { +			return status; +		} +	} +	 +	/* The 5th contains the network address */ +	if (num_protocols >= 3 && binding->host) { +		if (is_ipaddress(binding->host)) { +			status = dcerpc_floor_set_rhs_data(mem_ctx, &tower->floors[4],  +							   binding->host); +		} else { +			/* note that we don't attempt to resolve the +			   name here - when we get a hostname here we +			   are in the client code, and want to put in +			   a wildcard all-zeros IP for the server to +			   fill in */ +			status = dcerpc_floor_set_rhs_data(mem_ctx, &tower->floors[4],  +							   "0.0.0.0"); +		} +		if (NT_STATUS_IS_ERR(status)) { +			return status; +		} +	} + +	return NT_STATUS_OK; +}  | 
