Exemplo n.º 1
0
static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx,
				   struct messaging_context *msg_ctx,
				   const char *domain_name,
				   uint32_t flags,
				   struct ip_service_name *dclist,
				   int num_dcs,
				   struct netr_DsRGetDCNameInfo **info)
{
	struct sockaddr_storage ss;
	struct ip_service ip_list;
	enum nbt_name_type name_type = NBT_NAME_LOGON;
	NTSTATUS status;
	int i;
	const char *dc_name = NULL;
	fstring tmp_dc_name;
	struct netlogon_samlogon_response *r = NULL;
	bool store_cache = false;
	uint32_t nt_version = NETLOGON_NT_VERSION_1 |
			      NETLOGON_NT_VERSION_5 |
			      NETLOGON_NT_VERSION_5EX_WITH_IP;

	if (!msg_ctx) {
		msg_ctx = msg_context(mem_ctx);
	}

	if (flags & DS_PDC_REQUIRED) {
		name_type = NBT_NAME_PDC;
	}

	nt_version |= map_ds_flags_to_nt_version(flags);

	DEBUG(10,("process_dc_netbios\n"));

	for (i=0; i<num_dcs; i++) {

		ip_list.ss = dclist[i].ss;
		ip_list.port = 0;

		if (!interpret_string_addr(&ss, dclist[i].hostname, AI_NUMERICHOST)) {
			return NT_STATUS_UNSUCCESSFUL;
		}

		if (send_getdc_request(mem_ctx, msg_ctx,
				       &dclist[i].ss, domain_name,
				       NULL, nt_version))
		{
			int k;
			smb_msleep(300);
			for (k=0; k<5; k++) {
				if (receive_getdc_response(mem_ctx,
							   &dclist[i].ss,
							   domain_name,
							   &nt_version,
							   &dc_name,
							   &r)) {
					store_cache = true;
					namecache_store(dc_name, NBT_NAME_SERVER, 1, &ip_list);
					goto make_reply;
				}
				smb_msleep(1500);
			}
		}

		if (name_status_find(domain_name,
				     name_type,
				     NBT_NAME_SERVER,
				     &dclist[i].ss,
				     tmp_dc_name))
		{
			struct NETLOGON_SAM_LOGON_RESPONSE_NT40 logon1;

			r = TALLOC_ZERO_P(mem_ctx, struct netlogon_samlogon_response);
			NT_STATUS_HAVE_NO_MEMORY(r);

			ZERO_STRUCT(logon1);

			nt_version = NETLOGON_NT_VERSION_1;

			logon1.nt_version = nt_version;
			logon1.server = tmp_dc_name;
			logon1.domain = talloc_strdup_upper(mem_ctx, domain_name);
			NT_STATUS_HAVE_NO_MEMORY(logon1.domain);

			r->data.nt4 = logon1;
			r->ntver = nt_version;

			map_netlogon_samlogon_response(r);

			namecache_store(tmp_dc_name, NBT_NAME_SERVER, 1, &ip_list);

			goto make_reply;
		}
	}

	return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;

 make_reply:

	status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].ss,
					       &r->data.nt5_ex, info);
	if (NT_STATUS_IS_OK(status) && store_cache) {
		return store_cldap_reply(mem_ctx, flags, &dclist[i].ss,
					 nt_version, &r->data.nt5_ex);
	}

	return status;
}
Exemplo n.º 2
0
/* test UDP/138 ntlogon requests */
static bool nbt_test_ntlogon(struct torture_context *tctx)
{
	struct dgram_mailslot_handler *dgmslot;
	struct nbt_dgram_socket *dgmsock = nbt_dgram_socket_init(tctx, tctx->ev,
								 lp_iconv_convenience(tctx->lp_ctx));
	struct socket_address *dest;
	struct test_join *join_ctx;
	const struct dom_sid *dom_sid;
	struct cli_credentials *machine_credentials;

	const char *myaddress;
	struct nbt_netlogon_packet logon;
	struct nbt_netlogon_response *response;
	struct nbt_name myname;
	NTSTATUS status;
	struct timeval tv = timeval_current();

	struct socket_address *socket_address;
	const char *address;
	struct nbt_name name;

	struct interface *ifaces;
	
	name.name = lp_workgroup(tctx->lp_ctx);
	name.type = NBT_NAME_LOGON;
	name.scope = NULL;

	/* do an initial name resolution to find its IP */
	torture_assert_ntstatus_ok(tctx, 
				   resolve_name(lp_resolve_context(tctx->lp_ctx), &name, tctx, &address, tctx->ev),
				   talloc_asprintf(tctx, "Failed to resolve %s", name.name));

	load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
	myaddress = talloc_strdup(dgmsock, iface_best_ip(ifaces, address));

	socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
						     myaddress, lp_dgram_port(tctx->lp_ctx));
	torture_assert(tctx, socket_address != NULL, "Error getting address");

	/* try receiving replies on port 138 first, which will only
	   work if we are root and smbd/nmbd are not running - fall
	   back to listening on any port, which means replies from
	   most windows versions won't be seen */
	status = socket_listen(dgmsock->sock, socket_address, 0, 0);
	if (!NT_STATUS_IS_OK(status)) {
		talloc_free(socket_address);
		socket_address = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name,
							     myaddress, 0);
		torture_assert(tctx, socket_address != NULL, "Error getting address");

		socket_listen(dgmsock->sock, socket_address, 0, 0);
	}

	join_ctx = torture_join_domain(tctx, TEST_NAME, 
				       ACB_WSTRUST, &machine_credentials);
	dom_sid = torture_join_sid(join_ctx);

	torture_assert(tctx, join_ctx != NULL,
		       talloc_asprintf(tctx, "Failed to join domain %s as %s\n",
		       		       lp_workgroup(tctx->lp_ctx), TEST_NAME));

	/* setup a temporary mailslot listener for replies */
	dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
				      netlogon_handler, NULL);
	

	ZERO_STRUCT(logon);
	logon.command = LOGON_SAM_LOGON_REQUEST;
	logon.req.logon.request_count = 0;
	logon.req.logon.computer_name = TEST_NAME;
	logon.req.logon.user_name     = TEST_NAME"$";
	logon.req.logon.mailslot_name = dgmslot->mailslot_name;
	logon.req.logon.acct_control  = ACB_WSTRUST;
	/* Try with a SID this time */
	logon.req.logon.sid           = *dom_sid;
	logon.req.logon.nt_version    = 1;
	logon.req.logon.lmnt_token    = 0xFFFF;
	logon.req.logon.lm20_token    = 0xFFFF;

	make_nbt_name_client(&myname, TEST_NAME);

	dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, 
					   address, lp_dgram_port(tctx->lp_ctx));
	torture_assert(tctx, dest != NULL, "Error getting address");
	status = dgram_mailslot_netlogon_send(dgmsock, 
					      &name, dest, 
					      NBT_MAILSLOT_NTLOGON, 
					      &myname, &logon);
	torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");

	while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
		event_loop_once(dgmsock->event_ctx);
	}

	response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);

	torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");

	torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
	map_netlogon_samlogon_response(&response->data.samlogon);

	torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command");

	torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response");


	/* setup a temporary mailslot listener for replies */
	dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
				      netlogon_handler, NULL);
	

	ZERO_STRUCT(logon);
	logon.command = LOGON_SAM_LOGON_REQUEST;
	logon.req.logon.request_count = 0;
	logon.req.logon.computer_name = TEST_NAME;
	logon.req.logon.user_name     = TEST_NAME"$";
	logon.req.logon.mailslot_name = dgmslot->mailslot_name;
	logon.req.logon.acct_control  = ACB_WSTRUST;
	/* Leave sid as all zero */
	logon.req.logon.nt_version    = 1;
	logon.req.logon.lmnt_token    = 0xFFFF;
	logon.req.logon.lm20_token    = 0xFFFF;

	make_nbt_name_client(&myname, TEST_NAME);

	dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, 
					   address, lp_dgram_port(tctx->lp_ctx));
	torture_assert(tctx, dest != NULL, "Error getting address");
	status = dgram_mailslot_netlogon_send(dgmsock, 
					      &name, dest, 
					      NBT_MAILSLOT_NTLOGON, 
					      &myname, &logon);
	torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");

	while (timeval_elapsed(&tv) < 5 && dgmslot->private_data == NULL) {
		event_loop_once(dgmsock->event_ctx);
	}

	response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);

	torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");

	torture_assert_int_equal(tctx, response->response_type, NETLOGON_SAMLOGON, "Got incorrect type of netlogon response");
	map_netlogon_samlogon_response(&response->data.samlogon);

	torture_assert_int_equal(tctx, response->data.samlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE, "Got incorrect netlogon response command");

	torture_assert_str_equal(tctx, response->data.samlogon.data.nt5_ex.user_name, TEST_NAME"$", "Got incorrect user in netlogon response");


	/* setup (another) temporary mailslot listener for replies */
	dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
				      netlogon_handler, NULL);
	
	ZERO_STRUCT(logon);
	logon.command = LOGON_PRIMARY_QUERY;
	logon.req.pdc.computer_name = TEST_NAME;
	logon.req.pdc.mailslot_name = dgmslot->mailslot_name;
	logon.req.pdc.unicode_name  = TEST_NAME;
	logon.req.pdc.nt_version    = 1;
	logon.req.pdc.lmnt_token    = 0xFFFF;
	logon.req.pdc.lm20_token    = 0xFFFF;

	make_nbt_name_client(&myname, TEST_NAME);

	dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, 
					   address, lp_dgram_port(tctx->lp_ctx));
	torture_assert(tctx, dest != NULL, "Error getting address");
	status = dgram_mailslot_netlogon_send(dgmsock, 
					      &name, dest, 
					      NBT_MAILSLOT_NTLOGON, 
					      &myname, &logon);
	torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");

	while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) {
		event_loop_once(dgmsock->event_ctx);
	}

	response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);

	torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");

	torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response");
	torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command");

	torture_leave_domain(tctx, join_ctx);

	/* setup (another) temporary mailslot listener for replies */
	dgmslot = dgram_mailslot_temp(dgmsock, NBT_MAILSLOT_GETDC,
				      netlogon_handler, NULL);
	
	ZERO_STRUCT(logon);
	logon.command = LOGON_PRIMARY_QUERY;
	logon.req.pdc.computer_name = TEST_NAME;
	logon.req.pdc.mailslot_name = dgmslot->mailslot_name;
	logon.req.pdc.unicode_name  = TEST_NAME;
	logon.req.pdc.nt_version    = 1;
	logon.req.pdc.lmnt_token    = 0xFFFF;
	logon.req.pdc.lm20_token    = 0xFFFF;

	make_nbt_name_client(&myname, TEST_NAME);

	dest = socket_address_from_strings(dgmsock, dgmsock->sock->backend_name, 
					   address, lp_dgram_port(tctx->lp_ctx));
	torture_assert(tctx, dest != NULL, "Error getting address");
	status = dgram_mailslot_netlogon_send(dgmsock, 
					      &name, dest, 
					      NBT_MAILSLOT_NTLOGON, 
					      &myname, &logon);
	torture_assert_ntstatus_ok(tctx, status, "Failed to send ntlogon request");

	while (timeval_elapsed(&tv) < 5 && !dgmslot->private_data) {
		event_loop_once(dgmsock->event_ctx);
	}

	response = talloc_get_type(dgmslot->private_data, struct nbt_netlogon_response);

	torture_assert(tctx, response != NULL, "Failed to receive a netlogon reply packet");

	torture_assert_int_equal(tctx, response->response_type, NETLOGON_GET_PDC, "Got incorrect type of ntlogon response");
	torture_assert_int_equal(tctx, response->data.get_pdc.command, NETLOGON_RESPONSE_FROM_PDC, "Got incorrect ntlogon response command");


	return true;
}
Exemplo n.º 3
0
static bool parse_getdc_response(
	struct packet_struct *packet,
	TALLOC_CTX *mem_ctx,
	const char *domain_name,
	uint32_t *nt_version,
	const char **dc_name,
	struct netlogon_samlogon_response **samlogon_response)
{
	DATA_BLOB blob;
	struct netlogon_samlogon_response *r;
	union dgram_message_body p;
	enum ndr_err_code ndr_err;
	NTSTATUS status;

	const char *returned_dc = NULL;
	const char *returned_domain = NULL;

	blob = data_blob_const(packet->packet.dgram.data,
			       packet->packet.dgram.datasize);
	if (blob.length < 4) {
		DEBUG(1, ("invalid length: %d\n", (int)blob.length));
		return false;
	}

	if (RIVAL(blob.data,0) != DGRAM_SMB) {
		DEBUG(1, ("invalid packet\n"));
		return false;
	}

	blob.data += 4;
	blob.length -= 4;

	ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB,
		       (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(1, ("failed to parse packet\n"));
		return false;
	}

	if (p.smb.smb_command != SMB_TRANSACTION) {
		DEBUG(1, ("invalid smb_command: %d\n", p.smb.smb_command));
		return false;
	}

	if (DEBUGLEVEL >= 10) {
		NDR_PRINT_DEBUG(dgram_smb_packet, &p);
	}

	blob = p.smb.body.trans.data;

	r = talloc_zero(mem_ctx, struct netlogon_samlogon_response);
	if (!r) {
		return false;
	}

	status = pull_netlogon_samlogon_response(&blob, r, r);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(r);
		return false;
	}

	map_netlogon_samlogon_response(r);

	/* do we still need this ? */
	*nt_version = r->ntver;

	returned_domain = r->data.nt5_ex.domain_name;
	returned_dc = r->data.nt5_ex.pdc_name;

	if (!strequal(returned_domain, domain_name)) {
		DEBUG(3, ("GetDC: Expected domain %s, got %s\n",
			  domain_name, returned_domain));
		TALLOC_FREE(r);
		return false;
	}

	if (*returned_dc == '\\') returned_dc += 1;
	if (*returned_dc == '\\') returned_dc += 1;

	*dc_name = talloc_strdup(mem_ctx, returned_dc);
	if (!*dc_name) {
		TALLOC_FREE(r);
		return false;
	}

	if (samlogon_response) {
		*samlogon_response = r;
	} else {
		TALLOC_FREE(r);
	}

	DEBUG(10, ("GetDC gave name %s for domain %s\n",
		   *dc_name, returned_domain));

	return True;
}
Exemplo n.º 4
0
bool receive_getdc_response(TALLOC_CTX *mem_ctx,
			    struct sockaddr_storage *dc_ss,
			    const char *domain_name,
			    uint32_t *nt_version,
			    const char **dc_name,
			    struct netlogon_samlogon_response **_r)
{
	struct packet_struct *packet;
	const char *my_mailslot = NULL;
	struct in_addr dc_ip;
	DATA_BLOB blob;
	struct netlogon_samlogon_response r;
	union dgram_message_body p;
	enum ndr_err_code ndr_err;
	NTSTATUS status;

	const char *returned_dc = NULL;
	const char *returned_domain = NULL;

	if (dc_ss->ss_family != AF_INET) {
		return false;
	}

	dc_ip = ((struct sockaddr_in *)dc_ss)->sin_addr;

	my_mailslot = mailslot_name(mem_ctx, dc_ip);
	if (!my_mailslot) {
		return false;
	}

	packet = receive_unexpected(DGRAM_PACKET, 0, my_mailslot);

	if (packet == NULL) {
		DEBUG(5, ("Did not receive packet for %s\n", my_mailslot));
		return False;
	}

	DEBUG(5, ("Received packet for %s\n", my_mailslot));

	blob = data_blob_const(packet->packet.dgram.data,
			       packet->packet.dgram.datasize);

	if (blob.length < 4) {
		DEBUG(0,("invalid length: %d\n", (int)blob.length));
		return false;
	}

	if (RIVAL(blob.data,0) != DGRAM_SMB) {
		DEBUG(0,("invalid packet\n"));
		return false;
	}

	blob.data += 4;
	blob.length -= 4;

	ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB,
		       (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		DEBUG(0,("failed to parse packet\n"));
		return false;
	}

	if (p.smb.smb_command != SMB_TRANSACTION) {
		DEBUG(0,("invalid smb_command: %d\n", p.smb.smb_command));
		return false;
	}

	if (DEBUGLEVEL >= 10) {
		NDR_PRINT_DEBUG(dgram_smb_packet, &p);
	}

	blob = p.smb.body.trans.data;

	ZERO_STRUCT(r);

	status = pull_netlogon_samlogon_response(&blob, mem_ctx, &r);
	if (!NT_STATUS_IS_OK(status)) {
		return false;
	}

	map_netlogon_samlogon_response(&r);

	/* do we still need this ? */
	*nt_version = r.ntver;

	returned_domain = r.data.nt5_ex.domain;
	returned_dc = r.data.nt5_ex.pdc_name;

	if (!strequal(returned_domain, domain_name)) {
		DEBUG(3, ("GetDC: Expected domain %s, got %s\n",
			  domain_name, returned_domain));
		return false;
	}

	*dc_name = talloc_strdup(mem_ctx, returned_dc);
	if (!*dc_name) {
		return false;
	}

	if (**dc_name == '\\')	*dc_name += 1;
	if (**dc_name == '\\')	*dc_name += 1;

	if (_r) {
		*_r = (struct netlogon_samlogon_response *)talloc_memdup(
			mem_ctx, &r, sizeof(struct netlogon_samlogon_response));
		if (!*_r) {
			return false;
		}
	}

	DEBUG(10, ("GetDC gave name %s for domain %s\n",
		   *dc_name, returned_domain));

	return True;
}
Exemplo n.º 5
0
static NTSTATUS tcp_ldap_netlogon(void *conn,
				  TALLOC_CTX *mem_ctx,
				  struct cldap_netlogon *io)
{
	struct cldap_search search;
	struct ldap_SearchResEntry *res;
	NTSTATUS status;
	DATA_BLOB *blob;

	ZERO_STRUCT(search);
	search.in.attributes = (const char *[]) { "netlogon", NULL };
	search.in.filter =  cldap_netlogon_create_filter(mem_ctx, io);
	if (search.in.filter == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	status = tcp_ldap_rootdse(conn, mem_ctx, &search);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	res = search.out.response;
	if (res == NULL) {
		return NT_STATUS_NOT_FOUND;
	}

	if (res->num_attributes != 1 ||
	    strcasecmp(res->attributes[0].name, "netlogon") != 0 ||
	    res->attributes[0].num_values != 1 ||
	    res->attributes[0].values->length < 2) {
		return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
	}

	blob = res->attributes[0].values;
	status = pull_netlogon_samlogon_response(blob, mem_ctx,
						 &io->out.netlogon);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	if (io->in.map_response) {
		map_netlogon_samlogon_response(&io->out.netlogon);
	}

	return NT_STATUS_OK;
}

static NTSTATUS udp_ldap_rootdse(void *data, TALLOC_CTX *mem_ctx,
				 struct cldap_search *io)
{
	struct cldap_socket *cldap = talloc_get_type(data,
						     struct cldap_socket);

	return cldap_search(cldap, mem_ctx, io);
}

static bool test_netlogon_extra_attrs(struct torture_context *tctx,
				      request_rootdse_t request_rootdse,
				      void *conn)
{
	struct cldap_search io;
	NTSTATUS status;
	const char *attrs[] = {
		"netlogon",
		"supportedCapabilities",
		NULL
	};
	const char *attrs2[] = { "netlogon", "*", NULL };
	struct ldb_message ldbmsg = { NULL, 0, NULL };

	ZERO_STRUCT(io);
	io.in.dest_address = NULL;
	io.in.dest_port = 0;
	io.in.timeout   = 2;
	io.in.retries   = 2;
	/* Additional attributes may be requested next to netlogon */
	torture_comment(tctx, "Requesting netlogon with additional attribute\n");
	io.in.filter =
		talloc_asprintf(tctx, "(&"
				"(NtVer=%s)(AAC=%s)"
				/* Query for LDAP_CAP_ACTIVE_DIRECTORY_OID */
				"(supportedCapabilities=1.2.840.113556.1.4.800)"
				")",
				ldap_encode_ndr_uint32(tctx,
						       NETLOGON_NT_VERSION_5EX),
				ldap_encode_ndr_uint32(tctx, 0));
	torture_assert(tctx, io.in.filter != NULL, "OOM");
	io.in.attributes = attrs;
	status = request_rootdse(conn, tctx, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	torture_assert(tctx, io.out.response != NULL, "No Entries found.");
	CHECK_VAL(io.out.response->num_attributes, 2);

	/* netlogon + '*' attr return zero results */
	torture_comment(tctx, "Requesting netlogon and '*' attributes\n");
	io.in.attributes = attrs2;
	status = request_rootdse(conn, tctx, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	torture_assert(tctx, io.out.response != NULL, "No Entries found.");
	ldbmsg.num_elements = io.out.response->num_attributes;
	ldbmsg.elements = io.out.response->attributes;
	torture_assert(tctx, ldb_msg_find_element(&ldbmsg, "netlogon") != NULL,
		       "Attribute netlogon not found in Result Entry\n");

	/* Wildcards are not allowed in filters when netlogon is requested. */
	torture_comment(tctx, "Requesting netlogon with invalid attr filter\n");
	io.in.filter =
		talloc_asprintf(tctx,
				"(&(NtVer=%s)(AAC=%s)(supportedCapabilities=*))",
				ldap_encode_ndr_uint32(tctx,
						       NETLOGON_NT_VERSION_5EX),
				ldap_encode_ndr_uint32(tctx, 0));
	torture_assert(tctx, io.in.filter != NULL, "OOM");
	io.in.attributes = attrs;
	status = request_rootdse(conn, tctx, &io);
	CHECK_STATUS(status, NT_STATUS_OK);
	torture_assert(tctx, io.out.response == NULL,
		       "A wildcard filter should return no entries.");

	return true;
}


bool torture_netlogon_tcp(struct torture_context *tctx)
{
	const char *host = torture_setting_string(tctx, "host", NULL);
	bool ret = true;
	NTSTATUS status;
	struct ldap_connection *conn;
	TALLOC_CTX *mem_ctx;
	const char *url;

	mem_ctx = talloc_init("torture_ldap_netlogon");

	url = talloc_asprintf(mem_ctx, "ldap://%s/", host);

	status = torture_ldap_connection(tctx, &conn, url);
	if (!NT_STATUS_IS_OK(status)) {
		return false;
	}

	ret &= test_ldap_netlogon(tctx, tcp_ldap_netlogon, conn, host);
	ret &= test_ldap_netlogon_flags(tctx, tcp_ldap_netlogon, conn, host);
	ret &= test_netlogon_extra_attrs(tctx, tcp_ldap_rootdse, conn);

	return ret;
}

static NTSTATUS udp_ldap_netlogon(void *data,
				  TALLOC_CTX *mem_ctx,
				  struct cldap_netlogon *io)
{
	struct cldap_socket *cldap = talloc_get_type(data,
						     struct cldap_socket);

	return cldap_netlogon(cldap, mem_ctx, io);
}

bool torture_netlogon_udp(struct torture_context *tctx)
{
	const char *host = torture_setting_string(tctx, "host", NULL);
	bool ret = true;
	int r;
	struct cldap_socket *cldap;
	NTSTATUS status;
	struct tsocket_address *dest_addr;

	r = tsocket_address_inet_from_strings(tctx, "ip",
					      host,
					      lpcfg_cldap_port(tctx->lp_ctx),
					      &dest_addr);
	CHECK_VAL(r, 0);

	/* cldap_socket_init should now know about the dest. address */
	status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
	CHECK_STATUS(status, NT_STATUS_OK);

	ret &= test_ldap_netlogon(tctx, udp_ldap_netlogon, cldap, host);
	ret &= test_ldap_netlogon_flags(tctx, udp_ldap_netlogon, cldap, host);
	ret &= test_netlogon_extra_attrs(tctx, udp_ldap_rootdse, cldap);

	return ret;
}