/*
   Unix SMB/CIFS implementation.

   Copyright (C) Stefan Metzmacher <metze@samba.org> 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 "libnet/libnet.h"
#include "libcli/composite/composite.h"
#include "libcli/cldap/cldap.h"
#include "lib/ldb/include/ldb.h"
#include "lib/ldb/include/ldb_errors.h"
#include "lib/ldb_wrap.h"
#include "dsdb/samdb/samdb.h"
#include "../libds/common/flags.h"
#include "librpc/gen_ndr/ndr_drsuapi_c.h"
#include "libcli/security/security.h"
#include "librpc/gen_ndr/ndr_misc.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "librpc/gen_ndr/ndr_nbt.h"
#include "librpc/gen_ndr/ndr_drsuapi.h"
#include "auth/gensec/gensec.h"
#include "param/param.h"

/*****************************************************************************
 * Windows 2003 (w2k3) does the following steps when changing the server role
 * from domain member to domain controller
 *
 * We mostly do the same.
 *****************************************************************************/

/*
 * lookup DC:
 * - using nbt name<1C> request and a samlogon mailslot request
 * or
 * - using a DNS SRV _ldap._tcp.dc._msdcs. request and a CLDAP netlogon request
 *
 * see: becomeDC_recv_cldap() and becomeDC_send_cldap()
 */

/*
 * Open 1st LDAP connection to the DC using admin credentials
 *
 * see: becomeDC_connect_ldap1() and becomeDC_ldap_connect()
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_rootdse()
 *
 * Request:
 *	basedn:	""
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	*
 * Result:
 *      ""
 *		currentTime:		20061202155100.0Z
 *		subschemaSubentry:	CN=Aggregate,CN=Schema,CN=Configuration,<domain_partition>
 *		dsServiceName:		CN=<netbios_name>,CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *		namingContexts:		<domain_partition>
 *					CN=Configuration,<domain_partition>
 *					CN=Schema,CN=Configuration,<domain_partition>
 *		defaultNamingContext:	<domain_partition>
 *		schemaNamingContext:	CN=Schema,CN=Configuration,<domain_partition>
 *		configurationNamingContext:CN=Configuration,<domain_partition>
 *		rootDomainNamingContext:<domain_partition>
 *		supportedControl:	...
 *		supportedLDAPVersion:	3
 *					2
 *		supportedLDAPPolicies:	...
 *		highestCommitedUSN:	...
 *		supportedSASLMechanisms:GSSAPI
 *					GSS-SPNEGO
 *					EXTERNAL
 *					DIGEST-MD5
 *		dnsHostName:		<dns_host_name>
 *		ldapServiceName:	<domain_dns_name>:<netbios_name>$@<REALM>
 *		serverName:		CN=Servers,CN=<site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *		supportedCapabilities:	...
 *		isSyncronized:		TRUE
 *		isGlobalCatalogReady:	TRUE
 *		domainFunctionality:	0
 *		forestFunctionality:	0
 *		domainControllerFunctionality: 2
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_crossref_behavior_version()
 *
 * Request:
 *	basedn:	CN=Configuration,<domain_partition>
 *	scope:	one
 *	filter:	(cn=Partitions)
 *	attrs:	msDS-Behavior-Version
 * Result:
 *      CN=Partitions,CN=Configuration,<domain_partition>
 *		msDS-Behavior-Version:	0
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * NOTE: this seems to be a bug! as the messageID of the LDAP message is corrupted!
 *
 * not implemented here
 * 
 * Request:
 *	basedn:	CN=Schema,CN=Configuration,<domain_partition>
 *	scope:	one
 *	filter:	(cn=Partitions)
 *	attrs:	msDS-Behavior-Version
 * Result:
 *	<none>
 *
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_domain_behavior_version()
 * 
 * Request:
 *	basedn:	<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	msDS-Behavior-Version
 * Result:
 *	<domain_partition>
 *		msDS-Behavior-Version:	0
 */

/*
 * LDAP search 1st LDAP connection:
 * 
 * see: becomeDC_ldap1_schema_object_version()
 *
 * Request:
 *	basedn:	CN=Schema,CN=Configuration,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	objectVersion
 * Result:
 *	CN=Schema,CN=Configuration,<domain_partition>
 *		objectVersion:	30
 */

/*
 * LDAP search 1st LDAP connection:
 * 
 * not implemented, because the information is already there
 *
 * Request:
 *	basedn:	""
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	defaultNamingContext
 *		dnsHostName
 * Result:
 *	""
 *		defaultNamingContext:	<domain_partition>
 *		dnsHostName:		<dns_host_name>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_infrastructure_fsmo()
 * 
 * Request:
 *	basedn:	<WKGUID=2fbac1870ade11d297c400c04fd8d5cd,domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	1.1
 * Result:
 *	CN=Infrastructure,<domain_partition>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_w2k3_update_revision()
 *
 * Request:
 *	basedn:	CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	revision
 * Result:
 *      CN=Windows2003Update,CN=DomainUpdates,CN=System,<domain_partition>
 *		revision:	8
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_infrastructure_fsmo()
 *
 * Request:
 *	basedn:	CN=Infrastructure,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	fSMORoleOwner
 * Result:
 *      CN=Infrastructure,<domain_partition>
 *		fSMORoleOwner:	CN=NTDS Settings,<infrastructure_fsmo_server_object>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_infrastructure_fsmo()
 *
 * Request:
 *	basedn:	<infrastructure_fsmo_server_object>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	dnsHostName
 * Result:
 *      <infrastructure_fsmo_server_object>
 *		dnsHostName:	<dns_host_name>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_infrastructure_fsmo()
 *
 * Request:
 *	basedn:	CN=NTDS Settings,<infrastructure_fsmo_server_object>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	objectGUID
 * Result:
 *      CN=NTDS Settings,<infrastructure_fsmo_server_object>
 *		objectGUID:	<object_guid>
 */

/*
 * LDAP search 1st LDAP connection:
 * 
 * see: becomeDC_ldap1_rid_manager_fsmo()
 *
 * Request:
 *	basedn:	<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	rIDManagerReference
 * Result:
 *	<domain_partition>
 *		rIDManagerReference:	CN=RID Manager$,CN=System,<domain_partition>
 */

/*
 * LDAP search 1st LDAP connection:
 * 
 * see: becomeDC_ldap1_rid_manager_fsmo()
 *
 * Request:
 *	basedn:	CN=RID Manager$,CN=System,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	fSMORoleOwner
 * Result:
 *      CN=Infrastructure,<domain_partition>
 *		fSMORoleOwner:	CN=NTDS Settings,<rid_manager_fsmo_server_object>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_rid_manager_fsmo()
 *
 * Request:
 *	basedn:	<rid_manager_fsmo_server_object>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	dnsHostName
 * Result:
 *      <rid_manager_fsmo_server_object>
 *		dnsHostName:	<dns_host_name>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_rid_manager_fsmo()
 *
 * Request:
 *	basedn:	CN=NTDS Settings,<rid_manager_fsmo_server_object>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	msDs-ReplicationEpoch
 * Result:
 *      CN=NTDS Settings,<rid_manager_fsmo_server_object>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_site_object()
 *
 * Request:
 *	basedn:	CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:
 * Result:
 *      CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *		objectClass:	top
 *				site
 *		cn:		<new_dc_site_name>
 *		distinguishedName:CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *		instanceType:	4
 *		whenCreated:	...
 *		whenChanged:	...
 *		uSNCreated:	...
 *		uSNChanged:	...
 *		showInAdvancedViewOnly:	TRUE
 *		name:		<new_dc_site_name>
 *		objectGUID:	<object_guid>
 *		systemFlags:	1107296256 <0x42000000>
 *		objectCategory:	CN=Site,C=Schema,CN=Configuration,<domain_partition>
 */

/***************************************************************
 * Add this stage we call the check_options() callback function
 * of the caller, to see if he wants us to continue
 *
 * see: becomeDC_check_options()
 ***************************************************************/

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_computer_object()
 *
 * Request:
 *	basedn:	<domain_partition>
 *	scope:	sub
 *	filter:	(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
 *	attrs:	distinguishedName
 *		userAccountControl
 * Result:
 *      CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *		distinguishedName:	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *		userAccoountControl:	4096 <0x1000>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_server_object_1()
 *
 * Request:
 *	basedn:	CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:
 * Result:
 *      <noSuchObject>
 *	<matchedDN:CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * see: becomeDC_ldap1_server_object_2()
 * 
 * Request:
 *	basedn:	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	serverReferenceBL
 *	typesOnly: TRUE!!!
 * Result:
 *      CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 */

/*
 * LDAP add 1st LDAP connection:
 * 
 * see: becomeDC_ldap1_server_object_add()
 *
 * Request:
 *	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *	objectClass:	server
 *	systemFlags:	50000000 <0x2FAF080>
 *	serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 * Result:
 *      <success>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * not implemented, maybe we can add that later
 *
 * Request:
 *	basedn:	CN=NTDS Settings,CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:
 * Result:
 *      <noSuchObject>
 *	<matchedDN:CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
 */

/*
 * LDAP search 1st LDAP connection:
 *
 * not implemented because it gives no new information
 * 
 * Request:
 *	basedn:	CN=Partitions,CN=Configuration,<domain_partition>
 *	scope:	sub
 *	filter:	(nCName=<domain_partition>)
 *	attrs:	nCName
 *		dnsRoot
 *	controls: LDAP_SERVER_EXTENDED_DN_OID:critical=false
 * Result:
 *      <GUID=<hex_guid>>;CN=<domain_netbios_name>,CN=Partitions,<domain_partition>>
 *		nCName:		<GUID=<hex_guid>>;<SID=<hex_sid>>;<domain_partition>>
 *		dnsRoot:	<domain_dns_name>
 */

/*
 * LDAP modify 1st LDAP connection:
 *
 * see: becomeDC_ldap1_server_object_modify()
 * 
 * Request (add):
 *	CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
 *	serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 * Result:
 *	<attributeOrValueExist>
 */

/*
 * LDAP modify 1st LDAP connection:
 *
 * see: becomeDC_ldap1_server_object_modify()
 *
 * Request (replace):
 *	CN=<new_dc_netbios_name>,CN=Servers,CN=<new_dc_site_name>,CN=Sites,CN=Configuration,<domain_partition>>
 *	serverReference:CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 * Result:
 *	<success>
 */

/*
 * Open 1st DRSUAPI connection to the DC using admin credentials
 * DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
 * (w2k3 does 2 DsBind() calls here..., where is first is unused and contains garbage at the end)
 *
 * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi1_connect_recv(),
 *      becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv() and becomeDC_drsuapi1_bind_recv()
 */

/*
 * DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
 * on the 1st DRSUAPI connection
 *
 * see: becomeDC_drsuapi1_add_entry_send() and becomeDC_drsuapi1_add_entry_recv()
 */

/***************************************************************
 * Add this stage we call the prepare_db() callback function
 * of the caller, to see if he wants us to continue
 *
 * see: becomeDC_prepare_db()
 ***************************************************************/

/*
 * Open 2nd and 3rd DRSUAPI connection to the DC using admin credentials
 * - a DsBind with DRSUAPI_DS_BIND_GUID_W2K3 ("6afab99c-6e26-464a-975f-f58f105218bc")
 *   on the 2nd connection
 *
 * see: becomeDC_drsuapi_connect_send(), becomeDC_drsuapi2_connect_recv(),
 *      becomeDC_drsuapi_bind_send(), becomeDC_drsuapi_bind_recv(), becomeDC_drsuapi2_bind_recv()
 *	and becomeDC_drsuapi3_connect_recv()
 */

/*
 * replicate CN=Schema,CN=Configuration,...
 * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
 *
 * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
 *	becomeDC_drsuapi3_pull_schema_send() and becomeDC_drsuapi3_pull_schema_recv()
 *
 ***************************************************************
 * Add this stage we call the schema_chunk() callback function
 * for each replication message
 ***************************************************************/

/*
 * replicate CN=Configuration,...
 * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
 *
 * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
 *	becomeDC_drsuapi3_pull_config_send() and becomeDC_drsuapi3_pull_config_recv()
 *
 ***************************************************************
 * Add this stage we call the config_chunk() callback function
 * for each replication message
 ***************************************************************/

/*
 * LDAP unbind on the 1st LDAP connection
 *
 * not implemented, because it's not needed...
 */

/*
 * Open 2nd LDAP connection to the DC using admin credentials
 *
 * see: becomeDC_connect_ldap2() and becomeDC_ldap_connect()
 */

/*
 * LDAP search 2nd LDAP connection:
 * 
 * not implemented because it gives no new information
 * same as becomeDC_ldap1_computer_object()
 *
 * Request:
 *	basedn:	<domain_partition>
 *	scope:	sub
 *	filter:	(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<new_dc_account_name>))
 *	attrs:	distinguishedName
 *		userAccountControl
 * Result:
 *      CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *		distinguishedName:	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *		userAccoountControl:	4096 <0x00001000>
 */

/*
 * LDAP search 2nd LDAP connection:
 * 
 * not implemented because it gives no new information
 * same as becomeDC_ldap1_computer_object()
 *
 * Request:
 *	basedn:	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	userAccountControl
 * Result:
 *      CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *		userAccoountControl:	4096 <0x00001000>
 */

/*
 * LDAP modify 2nd LDAP connection:
 *
 * see: becomeDC_ldap2_modify_computer()
 *
 * Request (replace):
 *	CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *	userAccoountControl:	532480 <0x82000>
 * Result:
 *	<success>
 */

/*
 * LDAP search 2nd LDAP connection:
 *
 * see: becomeDC_ldap2_move_computer()
 * 
 * Request:
 *	basedn:	<WKGUID=2fbac1870ade11d297c400c04fd8d5cd,<domain_partition>>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	1.1
 * Result:
 *	CN=Domain Controllers,<domain_partition>
 */

/*
 * LDAP search 2nd LDAP connection:
 *
 * not implemented because it gives no new information
 * 
 * Request:
 *	basedn:	CN=Domain Controllers,<domain_partition>
 *	scope:	base
 *	filter:	(objectClass=*)
 *	attrs:	distinguishedName
 * Result:
 *	CN=Domain Controller,<domain_partition>
 *		distinguishedName:	CN=Domain Controllers,<domain_partition>
 */

/*
 * LDAP modifyRDN 2nd LDAP connection:
 *
 * see: becomeDC_ldap2_move_computer()
 * 
 * Request:
 *      entry:		CN=<new_dc_netbios_name>,CN=Computers,<domain_partition>
 *	newrdn:		CN=<new_dc_netbios_name>
 *	deleteoldrdn:	TRUE
 *	newparent:	CN=Domain Controllers,<domain_partition>
 * Result:
 *	<success>
 */

/*
 * LDAP unbind on the 2nd LDAP connection
 *
 * not implemented, because it's not needed...
 */

/*
 * replicate Domain Partition
 * on the 3rd DRSUAPI connection and the bind_handle from the 2nd connection
 *
 * see: becomeDC_drsuapi_pull_partition_send(), becomeDC_drsuapi_pull_partition_recv(),
 *	becomeDC_drsuapi3_pull_domain_send() and becomeDC_drsuapi3_pull_domain_recv()
 *
 ***************************************************************
 * Add this stage we call the domain_chunk() callback function
 * for each replication message
 ***************************************************************/

/* call DsReplicaUpdateRefs() for all partitions like this:
 *     req1: struct drsuapi_DsReplicaUpdateRefsRequest1
 *
 *                 naming_context: struct drsuapi_DsReplicaObjectIdentifier
 *                     __ndr_size               : 0x000000ae (174)
 *                     __ndr_size_sid           : 0x00000000 (0)
 *                     guid                     : 00000000-0000-0000-0000-000000000000
 *                     sid                      : S-0-0
 *                     dn                       : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base'
 *
 *                 dest_dsa_dns_name        : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base'
 *           dest_dsa_guid            : 4a0df188-a0b8-47ea-bbe5-e614723f16dd
 *           options                  : 0x0000001c (28)
 *                 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION
 *                 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE
 *                 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
 *                 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE
 *                 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010
 *
 * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the
 * CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
 * on the 2nd!!! DRSUAPI connection
 *
 * see:	becomeDC_drsuapi_update_refs_send(), becomeDC_drsuapi2_update_refs_schema_recv(),
 *	becomeDC_drsuapi2_update_refs_config_recv() and becomeDC_drsuapi2_update_refs_domain_recv()
 */

/*
 * Windows does opens the 4th and 5th DRSUAPI connection...
 * and does a DsBind() with the objectGUID from DsAddEntry() as bind_guid
 * on the 4th connection
 *
 * and then 2 full replications of the domain partition on the 5th connection
 * with the bind_handle from the 4th connection
 *
 * not implemented because it gives no new information
 */

struct libnet_BecomeDC_state {
	struct composite_context *creq;

	struct libnet_context *libnet;

	struct dom_sid zero_sid;

	struct {
		struct cldap_socket *sock;
		struct cldap_netlogon io;
		struct NETLOGON_SAM_LOGON_RESPONSE_EX netlogon;
	} cldap;

	struct becomeDC_ldap {
		struct ldb_context *ldb;
		const struct ldb_message *rootdse;
	} ldap1, ldap2;

	struct becomeDC_drsuapi {
		struct libnet_BecomeDC_state *s;
		struct dcerpc_binding *binding;
		struct dcerpc_pipe *pipe;
		DATA_BLOB gensec_skey;
		struct drsuapi_DsBind bind_r;
		struct GUID bind_guid;
		struct drsuapi_DsBindInfoCtr bind_info_ctr;
		struct drsuapi_DsBindInfo28 local_info28;
		struct drsuapi_DsBindInfo28 remote_info28;
		struct policy_handle bind_handle;
	} drsuapi1, drsuapi2, drsuapi3;

	struct libnet_BecomeDC_Domain domain;
	struct libnet_BecomeDC_Forest forest;
	struct libnet_BecomeDC_SourceDSA source_dsa;
	struct libnet_BecomeDC_DestDSA dest_dsa;

	struct libnet_BecomeDC_Partition schema_part, config_part, domain_part;

	struct becomeDC_fsmo {
		const char *dns_name;
		const char *server_dn_str;
		const char *ntds_dn_str;
		struct GUID ntds_guid;
	} infrastructure_fsmo;

	struct becomeDC_fsmo rid_manager_fsmo;

	struct libnet_BecomeDC_CheckOptions _co;
	struct libnet_BecomeDC_PrepareDB _pp;
	struct libnet_BecomeDC_StoreChunk _sc;
	struct libnet_BecomeDC_Callbacks callbacks;
};

static void becomeDC_recv_cldap(struct tevent_req *req);

static void becomeDC_send_cldap(struct libnet_BecomeDC_state *s)
{
	struct composite_context *c = s->creq;
	struct tevent_req *req;

	s->cldap.io.in.dest_address	= s->source_dsa.address;
	s->cldap.io.in.dest_port	= lp_cldap_port(s->libnet->lp_ctx);
	s->cldap.io.in.realm		= s->domain.dns_name;
	s->cldap.io.in.host		= s->dest_dsa.netbios_name;
	s->cldap.io.in.user		= NULL;
	s->cldap.io.in.domain_guid	= NULL;
	s->cldap.io.in.domain_sid	= NULL;
	s->cldap.io.in.acct_control	= -1;
	s->cldap.io.in.version		= NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
	s->cldap.io.in.map_response	= true;

	c->status = cldap_socket_init(s, s->libnet->event_ctx,
				      NULL, NULL, &s->cldap.sock);//TODO
	if (!composite_is_ok(c)) return;

	req = cldap_netlogon_send(s, s->cldap.sock, &s->cldap.io);
	if (composite_nomem(req, c)) return;
	tevent_req_set_callback(req, becomeDC_recv_cldap, s);
}

static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s);

static void becomeDC_recv_cldap(struct tevent_req *req)
{
	struct libnet_BecomeDC_state *s = tevent_req_callback_data(req,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;

	c->status = cldap_netlogon_recv(req,
					lp_iconv_convenience(s->libnet->lp_ctx),
					s, &s->cldap.io);
	talloc_free(req);
	if (!composite_is_ok(c)) return;

	s->cldap.netlogon = s->cldap.io.out.netlogon.data.nt5_ex;

	s->domain.dns_name		= s->cldap.netlogon.dns_domain;
	s->domain.netbios_name		= s->cldap.netlogon.domain;
	s->domain.guid			= s->cldap.netlogon.domain_uuid;

	s->forest.dns_name		= s->cldap.netlogon.forest;

	s->source_dsa.dns_name		= s->cldap.netlogon.pdc_dns_name;
	s->source_dsa.netbios_name	= s->cldap.netlogon.pdc_name;
	s->source_dsa.site_name		= s->cldap.netlogon.server_site;

	s->dest_dsa.site_name		= s->cldap.netlogon.client_site;

	becomeDC_connect_ldap1(s);
}

static NTSTATUS becomeDC_ldap_connect(struct libnet_BecomeDC_state *s, 
				      struct becomeDC_ldap *ldap)
{
	char *url;

	url = talloc_asprintf(s, "ldap://%s/", s->source_dsa.dns_name);
	NT_STATUS_HAVE_NO_MEMORY(url);

	ldap->ldb = ldb_wrap_connect(s, s->libnet->event_ctx, s->libnet->lp_ctx, url,
				     NULL,
				     s->libnet->cred,
				     0, NULL);
	talloc_free(url);
	if (ldap->ldb == NULL) {
		return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
	}

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_rootdse(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"*",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, NULL);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
			 "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->ldap1.rootdse = r->msgs[0];

	s->domain.dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "defaultNamingContext", NULL);
	if (!s->domain.dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;

	s->forest.root_dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "rootDomainNamingContext", NULL);
	if (!s->forest.root_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	s->forest.config_dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "configurationNamingContext", NULL);
	if (!s->forest.config_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	s->forest.schema_dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "schemaNamingContext", NULL);
	if (!s->forest.schema_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;

	s->source_dsa.server_dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "serverName", NULL);
	if (!s->source_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	s->source_dsa.ntds_dn_str	= ldb_msg_find_attr_as_string(s->ldap1.rootdse, "dsServiceName", NULL);
	if (!s->source_dsa.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_crossref_behavior_version(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"msDs-Behavior-Version",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.config_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_ONELEVEL, attrs,
			 "(cn=Partitions)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->forest.crossref_behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_domain_behavior_version(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"msDs-Behavior-Version",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
			 "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->domain.behavior_version = ldb_msg_find_attr_as_uint(r->msgs[0], "msDs-Behavior-Version", 0);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_schema_object_version(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"objectVersion",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->forest.schema_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
			 "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->forest.schema_object_version = ldb_msg_find_attr_as_uint(r->msgs[0], "objectVersion", 0);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_w2k3_update_revision(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"revision",
		NULL
	};

	basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=Windows2003Update,CN=DomainUpdates,CN=System,%s",
				s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE, attrs,
			 "(objectClass=*)");
	talloc_free(basedn);
	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
		/* w2k doesn't have this object */
		s->domain.w2k3_update_revision = 0;
		return NT_STATUS_OK;
	} else if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->domain.w2k3_update_revision = ldb_msg_find_attr_as_uint(r->msgs[0], "revision", 0);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_infrastructure_fsmo(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	struct ldb_dn *ntds_dn;
	struct ldb_dn *server_dn;
	static const char *_1_1_attrs[] = {
		"1.1",
		NULL
	};
	static const char *fsmo_attrs[] = {
		"fSMORoleOwner",
		NULL
	};
	static const char *dns_attrs[] = {
		"dnsHostName",
		NULL
	};
	static const char *guid_attrs[] = {
		"objectGUID",
		NULL
	};

	basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "<WKGUID=2fbac1870ade11d297c400c04fd8d5cd,%s>",
				s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 _1_1_attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	basedn = talloc_steal(s, r->msgs[0]->dn);
	talloc_free(r);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 fsmo_attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->infrastructure_fsmo.ntds_dn_str	= samdb_result_string(r->msgs[0], "fSMORoleOwner", NULL);
	if (!s->infrastructure_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->infrastructure_fsmo.ntds_dn_str);

	talloc_free(r);

	ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->infrastructure_fsmo.ntds_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(ntds_dn);

	server_dn = ldb_dn_get_parent(s, ntds_dn);
	NT_STATUS_HAVE_NO_MEMORY(server_dn);

	s->infrastructure_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
	NT_STATUS_HAVE_NO_MEMORY(s->infrastructure_fsmo.server_dn_str);

	ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
			 dns_attrs, "(objectClass=*)");
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->infrastructure_fsmo.dns_name	= samdb_result_string(r->msgs[0], "dnsHostName", NULL);
	if (!s->infrastructure_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->infrastructure_fsmo.dns_name);

	talloc_free(r);

	ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
			 guid_attrs, "(objectClass=*)");
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->infrastructure_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");

	talloc_free(r);

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_rid_manager_fsmo(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	const char *reference_dn_str;
	struct ldb_dn *ntds_dn;
	struct ldb_dn *server_dn;
	static const char *rid_attrs[] = {
		"rIDManagerReference",
		NULL
	};
	static const char *fsmo_attrs[] = {
		"fSMORoleOwner",
		NULL
	};
	static const char *dns_attrs[] = {
		"dnsHostName",
		NULL
	};
	static const char *guid_attrs[] = {
		"objectGUID",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 rid_attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	reference_dn_str	= samdb_result_string(r->msgs[0], "rIDManagerReference", NULL);
	if (!reference_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;

	basedn = ldb_dn_new(s, s->ldap1.ldb, reference_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	talloc_free(r);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 fsmo_attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->rid_manager_fsmo.ntds_dn_str	= samdb_result_string(r->msgs[0], "fSMORoleOwner", NULL);
	if (!s->rid_manager_fsmo.ntds_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->rid_manager_fsmo.ntds_dn_str);

	talloc_free(r);

	ntds_dn = ldb_dn_new(s, s->ldap1.ldb, s->rid_manager_fsmo.ntds_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(ntds_dn);

	server_dn = ldb_dn_get_parent(s, ntds_dn);
	NT_STATUS_HAVE_NO_MEMORY(server_dn);

	s->rid_manager_fsmo.server_dn_str = ldb_dn_alloc_linearized(s, server_dn);
	NT_STATUS_HAVE_NO_MEMORY(s->rid_manager_fsmo.server_dn_str);

	ret = ldb_search(s->ldap1.ldb, s, &r, server_dn, LDB_SCOPE_BASE,
			 dns_attrs, "(objectClass=*)");
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->rid_manager_fsmo.dns_name	= samdb_result_string(r->msgs[0], "dnsHostName", NULL);
	if (!s->rid_manager_fsmo.dns_name) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->rid_manager_fsmo.dns_name);

	talloc_free(r);

	ret = ldb_search(s->ldap1.ldb, s, &r, ntds_dn, LDB_SCOPE_BASE,
			 guid_attrs, "(objectClass=*)");
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->rid_manager_fsmo.ntds_guid = samdb_result_guid(r->msgs[0], "objectGUID");

	talloc_free(r);

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_site_object(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;

	basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Sites,%s",
				s->dest_dsa.site_name,
				s->forest.config_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 NULL, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->dest_dsa.site_guid = samdb_result_guid(r->msgs[0], "objectGUID");

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_check_options(struct libnet_BecomeDC_state *s)
{
	if (!s->callbacks.check_options) return NT_STATUS_OK;

	s->_co.domain		= &s->domain;
	s->_co.forest		= &s->forest;
	s->_co.source_dsa	= &s->source_dsa;

	return s->callbacks.check_options(s->callbacks.private_data, &s->_co);
}

static NTSTATUS becomeDC_ldap1_computer_object(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	static const char *attrs[] = {
		"distinguishedName",
		"userAccountControl",
		NULL
	};

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_SUBTREE, attrs,
			 "(&(|(objectClass=user)(objectClass=computer))(sAMAccountName=%s$))",
			 s->dest_dsa.netbios_name);
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	s->dest_dsa.computer_dn_str	= samdb_result_string(r->msgs[0], "distinguishedName", NULL);
	if (!s->dest_dsa.computer_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->dest_dsa.computer_dn_str);

	s->dest_dsa.user_account_control = samdb_result_uint(r->msgs[0], "userAccountControl", 0);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_server_object_1(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	const char *server_reference_dn_str;
	struct ldb_dn *server_reference_dn;
	struct ldb_dn *computer_dn;

	basedn = ldb_dn_new_fmt(s, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
				s->dest_dsa.netbios_name,
				s->dest_dsa.site_name,
				s->forest.config_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 NULL, "(objectClass=*)");
	talloc_free(basedn);
	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
		/* if the object doesn't exist, we'll create it later */
		return NT_STATUS_OK;
	} else if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	server_reference_dn_str = samdb_result_string(r->msgs[0], "serverReference", NULL);
	if (server_reference_dn_str) {
		server_reference_dn	= ldb_dn_new(r, s->ldap1.ldb, server_reference_dn_str);
		NT_STATUS_HAVE_NO_MEMORY(server_reference_dn);

		computer_dn		= ldb_dn_new(r, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
		NT_STATUS_HAVE_NO_MEMORY(computer_dn);

		/*
		 * if the server object belongs to another DC in another domain in the forest,
		 * we should not touch this object!
		 */
		if (ldb_dn_compare(computer_dn, server_reference_dn) != 0) {
			talloc_free(r);
			return NT_STATUS_OBJECT_NAME_COLLISION;
		}
	}

	/* if the server object is already for the dest_dsa, then we don't need to create it */
	s->dest_dsa.server_dn_str	= samdb_result_string(r->msgs[0], "distinguishedName", NULL);
	if (!s->dest_dsa.server_dn_str) return NT_STATUS_INVALID_NETWORK_RESPONSE;
	talloc_steal(s, s->dest_dsa.server_dn_str);

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_server_object_2(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	const char *server_reference_bl_dn_str;
	static const char *attrs[] = {
		"serverReferenceBL",
		NULL
	};

	/* if the server_dn_str has a valid value, we skip this lookup */
	if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;

	basedn = ldb_dn_new(s, s->ldap1.ldb, s->dest_dsa.computer_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap1.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	server_reference_bl_dn_str = samdb_result_string(r->msgs[0], "serverReferenceBL", NULL);
	if (!server_reference_bl_dn_str) {
		/* if no back link is present, we're done for this function */
		talloc_free(r);
		return NT_STATUS_OK;
	}

	/* if the server object is already for the dest_dsa, then we don't need to create it */
	s->dest_dsa.server_dn_str	= samdb_result_string(r->msgs[0], "serverReferenceBL", NULL);
	if (s->dest_dsa.server_dn_str) {
		/* if a back link is present, we know that the server object is present */
		talloc_steal(s, s->dest_dsa.server_dn_str);
	}

	talloc_free(r);
	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_server_object_add(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_message *msg;
	char *server_dn_str;

	/* if the server_dn_str has a valid value, we skip this lookup */
	if (s->dest_dsa.server_dn_str) return NT_STATUS_OK;

	msg = ldb_msg_new(s);
	NT_STATUS_HAVE_NO_MEMORY(msg);

	msg->dn = ldb_dn_new_fmt(msg, s->ldap1.ldb, "CN=%s,CN=Servers,CN=%s,CN=Sites,%s",
				 s->dest_dsa.netbios_name,
				 s->dest_dsa.site_name,
				 s->forest.config_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(msg->dn);

	ret = ldb_msg_add_string(msg, "objectClass", "server");
	if (ret != 0) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}
	ret = ldb_msg_add_string(msg, "systemFlags", "50000000");
	if (ret != 0) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}
	ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
	if (ret != 0) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}

	server_dn_str = ldb_dn_alloc_linearized(s, msg->dn);
	NT_STATUS_HAVE_NO_MEMORY(server_dn_str);

	ret = ldb_add(s->ldap1.ldb, msg);
	talloc_free(msg);
	if (ret != LDB_SUCCESS) {
		talloc_free(server_dn_str);
		return NT_STATUS_LDAP(ret);
	}

	s->dest_dsa.server_dn_str = server_dn_str;

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap1_server_object_modify(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_message *msg;
	uint32_t i;

	/* make a 'modify' msg, and only for serverReference */
	msg = ldb_msg_new(s);
	NT_STATUS_HAVE_NO_MEMORY(msg);
	msg->dn = ldb_dn_new(msg, s->ldap1.ldb, s->dest_dsa.server_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(msg->dn);

	ret = ldb_msg_add_string(msg, "serverReference", s->dest_dsa.computer_dn_str);
	if (ret != 0) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}

	/* mark all the message elements (should be just one)
	   as LDB_FLAG_MOD_ADD */
	for (i=0;i<msg->num_elements;i++) {
		msg->elements[i].flags = LDB_FLAG_MOD_ADD;
	}

	ret = ldb_modify(s->ldap1.ldb, msg);
	if (ret == LDB_SUCCESS) {
		talloc_free(msg);
		return NT_STATUS_OK;
	} else if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
		/* retry with LDB_FLAG_MOD_REPLACE */
	} else {
		talloc_free(msg);
		return NT_STATUS_LDAP(ret);
	}

	/* mark all the message elements (should be just one)
	   as LDB_FLAG_MOD_REPLACE */
	for (i=0;i<msg->num_elements;i++) {
		msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
	}

	ret = ldb_modify(s->ldap1.ldb, msg);
	talloc_free(msg);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	}

	return NT_STATUS_OK;
}

static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
					  struct becomeDC_drsuapi *drsuapi,
					  void (*recv_fn)(struct composite_context *req));
static void becomeDC_drsuapi1_connect_recv(struct composite_context *req);
static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s);

static void becomeDC_connect_ldap1(struct libnet_BecomeDC_state *s)
{
	struct composite_context *c = s->creq;

	c->status = becomeDC_ldap_connect(s, &s->ldap1);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_rootdse(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_crossref_behavior_version(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_domain_behavior_version(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_schema_object_version(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_w2k3_update_revision(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_infrastructure_fsmo(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_rid_manager_fsmo(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_site_object(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_check_options(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_computer_object(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_server_object_1(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_server_object_2(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_server_object_add(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap1_server_object_modify(s);
	if (!composite_is_ok(c)) return;

	becomeDC_drsuapi_connect_send(s, &s->drsuapi1, becomeDC_drsuapi1_connect_recv);
}

static void becomeDC_drsuapi_connect_send(struct libnet_BecomeDC_state *s,
					  struct becomeDC_drsuapi *drsuapi,
					  void (*recv_fn)(struct composite_context *req))
{
	struct composite_context *c = s->creq;
	struct composite_context *creq;
	char *binding_str;

	drsuapi->s = s;

	if (!drsuapi->binding) {
		const char *krb5_str = "";
		const char *print_str = "";
		/*
		 * Note: Replication only works with Windows 2000 when 'krb5' is
		 *       passed as auth_type here. If NTLMSSP is used, Windows
		 *       2000 returns garbage in the DsGetNCChanges() response
		 *       if encrypted password attributes would be in the response.
		 *       That means the replication of the schema and configuration
		 *       partition works fine, but it fails for the domain partition.
		 */
		if (lp_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
				 "force krb5", true))
		{
			krb5_str = "krb5,";
		}
		if (lp_parm_bool(s->libnet->lp_ctx, NULL, "become_dc",
				 "print", false))
		{
			print_str = "print,";
		}
		binding_str = talloc_asprintf(s, "ncacn_ip_tcp:%s[%s%sseal]",
					      s->source_dsa.dns_name,
					      krb5_str, print_str);
		if (composite_nomem(binding_str, c)) return;
		c->status = dcerpc_parse_binding(s, binding_str, &drsuapi->binding);
		talloc_free(binding_str);
		if (!composite_is_ok(c)) return;
	}

	creq = dcerpc_pipe_connect_b_send(s, drsuapi->binding, &ndr_table_drsuapi,
					  s->libnet->cred, s->libnet->event_ctx,
					  s->libnet->lp_ctx);
	composite_continue(c, creq, recv_fn, s);
}

static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
				       struct becomeDC_drsuapi *drsuapi,
				       void (*recv_fn)(struct rpc_request *req));
static void becomeDC_drsuapi1_bind_recv(struct rpc_request *req);

static void becomeDC_drsuapi1_connect_recv(struct composite_context *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;

	c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi1.pipe);
	if (!composite_is_ok(c)) return;

	c->status = gensec_session_key(s->drsuapi1.pipe->conn->security_state.generic_state,
				       &s->drsuapi1.gensec_skey);
	if (!composite_is_ok(c)) return;

	becomeDC_drsuapi_bind_send(s, &s->drsuapi1, becomeDC_drsuapi1_bind_recv);
}

static void becomeDC_drsuapi_bind_send(struct libnet_BecomeDC_state *s,
				       struct becomeDC_drsuapi *drsuapi,
				       void (*recv_fn)(struct rpc_request *req))
{
	struct composite_context *c = s->creq;
	struct rpc_request *req;
	struct drsuapi_DsBindInfo28 *bind_info28;

	GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &drsuapi->bind_guid);

	bind_info28				= &drsuapi->local_info28;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_BASE;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
	if (s->domain.behavior_version == 2) {
		/* TODO: find out how this is really triggered! */
		bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
	}
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_00100000;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
#if 0 /* we don't support XPRESS compression yet */
	bind_info28->supported_extensions	|= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
#endif
	bind_info28->site_guid			= s->dest_dsa.site_guid;
	bind_info28->pid			= 0;
	bind_info28->repl_epoch			= 0;

	drsuapi->bind_info_ctr.length		= 28;
	drsuapi->bind_info_ctr.info.info28	= *bind_info28;

	drsuapi->bind_r.in.bind_guid = &drsuapi->bind_guid;
	drsuapi->bind_r.in.bind_info = &drsuapi->bind_info_ctr;
	drsuapi->bind_r.out.bind_handle = &drsuapi->bind_handle;

	req = dcerpc_drsuapi_DsBind_send(drsuapi->pipe, s, &drsuapi->bind_r);
	composite_continue_rpc(c, req, recv_fn, s);
}

static WERROR becomeDC_drsuapi_bind_recv(struct libnet_BecomeDC_state *s,
					 struct becomeDC_drsuapi *drsuapi)
{
	if (!W_ERROR_IS_OK(drsuapi->bind_r.out.result)) {
		return drsuapi->bind_r.out.result;
	}

	ZERO_STRUCT(drsuapi->remote_info28);
	if (drsuapi->bind_r.out.bind_info) {
		switch (drsuapi->bind_r.out.bind_info->length) {
		case 24: {
			struct drsuapi_DsBindInfo24 *info24;
			info24 = &drsuapi->bind_r.out.bind_info->info.info24;
			drsuapi->remote_info28.supported_extensions	= info24->supported_extensions;
			drsuapi->remote_info28.site_guid		= info24->site_guid;
			drsuapi->remote_info28.pid			= info24->pid;
			drsuapi->remote_info28.repl_epoch		= 0;
			break;
		}
		case 48: {
			struct drsuapi_DsBindInfo48 *info48;
			info48 = &drsuapi->bind_r.out.bind_info->info.info48;
			drsuapi->remote_info28.supported_extensions	= info48->supported_extensions;
			drsuapi->remote_info28.site_guid		= info48->site_guid;
			drsuapi->remote_info28.pid			= info48->pid;
			drsuapi->remote_info28.repl_epoch		= info48->repl_epoch;
			break;
		}
		case 28:
			drsuapi->remote_info28 = drsuapi->bind_r.out.bind_info->info.info28;
			break;
		}
	}

	return WERR_OK;
}

static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s);

static void becomeDC_drsuapi1_bind_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	WERROR status;

	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsBind, &s->drsuapi1.bind_r);
	}

	status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi1);
	if (!W_ERROR_IS_OK(status)) {
		composite_error(c, werror_to_ntstatus(status));
		return;
	}

	becomeDC_drsuapi1_add_entry_send(s);
}

static void becomeDC_drsuapi1_add_entry_recv(struct rpc_request *req);

static void becomeDC_drsuapi1_add_entry_send(struct libnet_BecomeDC_state *s)
{
	struct composite_context *c = s->creq;
	struct rpc_request *req;
	struct drsuapi_DsAddEntry *r;
	struct drsuapi_DsReplicaObjectIdentifier *identifier;
	uint32_t num_attrs, i = 0;
	struct drsuapi_DsReplicaAttribute *attrs;
	struct smb_iconv_convenience *iconv_convenience = lp_iconv_convenience(s->libnet->lp_ctx);
	enum ndr_err_code ndr_err;
	bool w2k3;

	/* choose a random invocationId */
	s->dest_dsa.invocation_id = GUID_random();

	/*
	 * if the schema version indicates w2k3, then
	 * also send some w2k3 specific attributes
	 */
	if (s->forest.schema_object_version >= 30) {
		w2k3 = true;
	} else {
		w2k3 = false;
	}

	r = talloc_zero(s, struct drsuapi_DsAddEntry);
	if (composite_nomem(r, c)) return;

	/* setup identifier */
	identifier		= talloc(r, struct drsuapi_DsReplicaObjectIdentifier);
	if (composite_nomem(identifier, c)) return;
	identifier->guid	= GUID_zero();
	identifier->sid		= s->zero_sid;
	identifier->dn		= talloc_asprintf(identifier, "CN=NTDS Settings,%s",
						  s->dest_dsa.server_dn_str);
	if (composite_nomem(identifier->dn, c)) return;

	/* allocate attribute array */
	num_attrs	= 11;
	attrs		= talloc_array(r, struct drsuapi_DsReplicaAttribute, num_attrs);
	if (composite_nomem(attrs, c)) return;

	/* ntSecurityDescriptor */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct security_descriptor *v;
		struct dom_sid *domain_admins_sid;
		const char *domain_admins_sid_str;

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		domain_admins_sid = dom_sid_add_rid(vs, s->domain.sid, DOMAIN_RID_ADMINS);
		if (composite_nomem(domain_admins_sid, c)) return;

		domain_admins_sid_str = dom_sid_string(domain_admins_sid, domain_admins_sid);
		if (composite_nomem(domain_admins_sid_str, c)) return;

		v = security_descriptor_dacl_create(vd,
					       0,
					       /* owner: domain admins */
					       domain_admins_sid_str,
					       /* owner group: domain admins */
					       domain_admins_sid_str,
					       /* authenticated users */
					       SID_NT_AUTHENTICATED_USERS,
					       SEC_ACE_TYPE_ACCESS_ALLOWED,
					       SEC_STD_READ_CONTROL |
					       SEC_ADS_LIST |
					       SEC_ADS_READ_PROP |
					       SEC_ADS_LIST_OBJECT,
					       0,
					       /* domain admins */
					       domain_admins_sid_str,
					       SEC_ACE_TYPE_ACCESS_ALLOWED,
					       SEC_STD_REQUIRED |
					       SEC_ADS_CREATE_CHILD |
					       SEC_ADS_LIST |
					       SEC_ADS_SELF_WRITE |
					       SEC_ADS_READ_PROP |
					       SEC_ADS_WRITE_PROP |
					       SEC_ADS_DELETE_TREE |
					       SEC_ADS_LIST_OBJECT |
					       SEC_ADS_CONTROL_ACCESS,
					       0,
					       /* system */
					       SID_NT_SYSTEM,
					       SEC_ACE_TYPE_ACCESS_ALLOWED,
					       SEC_STD_REQUIRED |
					       SEC_ADS_CREATE_CHILD |
					       SEC_ADS_DELETE_CHILD |
					       SEC_ADS_LIST |
					       SEC_ADS_SELF_WRITE |
					       SEC_ADS_READ_PROP |
					       SEC_ADS_WRITE_PROP |
					       SEC_ADS_DELETE_TREE |
					       SEC_ADS_LIST_OBJECT |
					       SEC_ADS_CONTROL_ACCESS,
					       0,
					       /* end */
					       NULL);
		if (composite_nomem(v, c)) return;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, v,(ndr_push_flags_fn_t)ndr_push_security_descriptor);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_ntSecurityDescriptor;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* objectClass: nTDSDSA */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		vd[0] = data_blob_talloc(vd, NULL, 4);
		if (composite_nomem(vd[0].data, c)) return;

		/* value for nTDSDSA */
		SIVAL(vd[0].data, 0, 0x0017002F);

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_objectClass;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* objectCategory: CN=NTDS-DSA,CN=Schema,... */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[1];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= talloc_asprintf(vd, "CN=NTDS-DSA,%s",
							  s->forest.schema_dn_str);
		if (composite_nomem(v[0].dn, c)) return;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0], 
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_objectCategory;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* invocationId: random guid */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		const struct GUID *v;

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		v = &s->dest_dsa.invocation_id;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, v, (ndr_push_flags_fn_t)ndr_push_GUID);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_invocationId;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* hasMasterNCs: ... */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[3];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 3);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= s->forest.config_dn_str;

		v[1].guid		= GUID_zero();
		v[1].sid		= s->zero_sid;
		v[1].dn			= s->domain.dn_str;

		v[2].guid		= GUID_zero();
		v[2].sid		= s->zero_sid;
		v[2].dn			= s->forest.schema_dn_str;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		ndr_err = ndr_push_struct_blob(&vd[1], vd, iconv_convenience, &v[1],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		ndr_err = ndr_push_struct_blob(&vd[2], vd, iconv_convenience, &v[2],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];
		vs[1].blob		= &vd[1];
		vs[2].blob		= &vd[2];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_hasMasterNCs;
		attrs[i].value_ctr.num_values	= 3;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* msDS-hasMasterNCs: ... */
	if (w2k3) {
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[3];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 3);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 3);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= s->forest.config_dn_str;

		v[1].guid		= GUID_zero();
		v[1].sid		= s->zero_sid;
		v[1].dn			= s->domain.dn_str;

		v[2].guid		= GUID_zero();
		v[2].sid		= s->zero_sid;
		v[2].dn			= s->forest.schema_dn_str;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		ndr_err = ndr_push_struct_blob(&vd[1], vd, iconv_convenience, &v[1],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		ndr_err = ndr_push_struct_blob(&vd[2], vd, iconv_convenience, &v[2],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];
		vs[1].blob		= &vd[1];
		vs[2].blob		= &vd[2];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_msDS_hasMasterNCs;
		attrs[i].value_ctr.num_values	= 3;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* dMDLocation: CN=Schema,... */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[1];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= s->forest.schema_dn_str;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_dMDLocation;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* msDS-HasDomainNCs: <domain_partition> */
	if (w2k3) {
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[1];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= s->domain.dn_str;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_msDS_HasDomainNCs;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* msDS-Behavior-Version */
	if (w2k3) {
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		vd[0] = data_blob_talloc(vd, NULL, 4);
		if (composite_nomem(vd[0].data, c)) return;

		SIVAL(vd[0].data, 0, 
		      lp_parm_int(s->libnet->lp_ctx, NULL, "ads", "functional level", DS_DC_FUNCTION_2008));

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_msDS_Behavior_Version;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* systemFlags */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		vd[0] = data_blob_talloc(vd, NULL, 4);
		if (composite_nomem(vd[0].data, c)) return;

		SIVAL(vd[0].data, 0, SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_systemFlags;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* serverReference: ... */
	{
		struct drsuapi_DsAttributeValue *vs;
		DATA_BLOB *vd;
		struct drsuapi_DsReplicaObjectIdentifier3 v[1];

		vs = talloc_array(attrs, struct drsuapi_DsAttributeValue, 1);
		if (composite_nomem(vs, c)) return;

		vd = talloc_array(vs, DATA_BLOB, 1);
		if (composite_nomem(vd, c)) return;

		v[0].guid		= GUID_zero();
		v[0].sid		= s->zero_sid;
		v[0].dn			= s->dest_dsa.computer_dn_str;

		ndr_err = ndr_push_struct_blob(&vd[0], vd, iconv_convenience, &v[0],
					       (ndr_push_flags_fn_t)ndr_push_drsuapi_DsReplicaObjectIdentifier3);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			c->status = ndr_map_error2ntstatus(ndr_err);
			if (!composite_is_ok(c)) return;
		}

		vs[0].blob		= &vd[0];

		attrs[i].attid			= DRSUAPI_ATTRIBUTE_serverReference;
		attrs[i].value_ctr.num_values	= 1;
		attrs[i].value_ctr.values	= vs;

		i++;
	}

	/* truncate the attribute list to the attribute count we have filled in */
	num_attrs = i;

	/* setup request structure */
	r->in.bind_handle						= &s->drsuapi1.bind_handle;
	r->in.level							= 2;
	r->in.req							= talloc(s, union drsuapi_DsAddEntryRequest);
	r->in.req->req2.first_object.next_object			= NULL;
	r->in.req->req2.first_object.object.identifier			= identifier;
	r->in.req->req2.first_object.object.flags			= 0x00000000;
	r->in.req->req2.first_object.object.attribute_ctr.num_attributes= num_attrs;
	r->in.req->req2.first_object.object.attribute_ctr.attributes	= attrs;

	r->out.level_out	= talloc(s, int32_t);
	r->out.ctr		= talloc(s, union drsuapi_DsAddEntryCtr);

	req = dcerpc_drsuapi_DsAddEntry_send(s->drsuapi1.pipe, r, r);
	composite_continue_rpc(c, req, becomeDC_drsuapi1_add_entry_recv, s);
}

static void becomeDC_drsuapi2_connect_recv(struct composite_context *req);
static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s);

static void becomeDC_drsuapi1_add_entry_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsAddEntry *r = talloc_get_type(req->ndr.struct_ptr,
				       struct drsuapi_DsAddEntry);
	char *binding_str;
	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsAddEntry, r);
	}

	if (!W_ERROR_IS_OK(r->out.result)) {
		composite_error(c, werror_to_ntstatus(r->out.result));
		return;
	}

	if (*r->out.level_out == 3) {
		if (r->out.ctr->ctr3.count != 1) {
			WERROR status;

			if (r->out.ctr->ctr3.level != 1) {
				composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
				return;
			}

			if (!r->out.ctr->ctr3.error) {
				composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
				return;
			}

			status = r->out.ctr->ctr3.error->info1.status;

			if (!r->out.ctr->ctr3.error->info1.info) {
				composite_error(c, werror_to_ntstatus(status));
				return;
			}

			/* see if we can get a more detailed error */
			switch (r->out.ctr->ctr3.error->info1.level) {
			case 1:
				status = r->out.ctr->ctr3.error->info1.info->error1.status;
				break;
			case 4:
			case 5:
			case 6:
			case 7:
				status = r->out.ctr->ctr3.error->info1.info->errorX.status;
				break;
			}

			composite_error(c, werror_to_ntstatus(status));
			return;
		}

		s->dest_dsa.ntds_guid	= r->out.ctr->ctr3.objects[0].guid;
	} else if (*r->out.level_out == 2) {
		if (r->out.ctr->ctr2.count != 1) {
			composite_error(c, werror_to_ntstatus(r->out.ctr->ctr2.error.status));
			return;
		}

		s->dest_dsa.ntds_guid	= r->out.ctr->ctr2.objects[0].guid;
	} else {
		composite_error(c, NT_STATUS_INVALID_NETWORK_RESPONSE);
		return;
	}

	talloc_free(r);

	s->dest_dsa.ntds_dn_str = talloc_asprintf(s, "CN=NTDS Settings,%s",
						  s->dest_dsa.server_dn_str);
	if (composite_nomem(s->dest_dsa.ntds_dn_str, c)) return;

	c->status = becomeDC_prepare_db(s);
	if (!composite_is_ok(c)) return;

	/* this avoids the epmapper lookup on the 2nd connection */
	binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
	if (composite_nomem(binding_str, c)) return;

	c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi2.binding);
	talloc_free(binding_str);
	if (!composite_is_ok(c)) return;

	/* w2k3 uses the same assoc_group_id as on the first connection, so we do */
	s->drsuapi2.binding->assoc_group_id	= s->drsuapi1.pipe->assoc_group_id;

	becomeDC_drsuapi_connect_send(s, &s->drsuapi2, becomeDC_drsuapi2_connect_recv);
}

static NTSTATUS becomeDC_prepare_db(struct libnet_BecomeDC_state *s)
{
	if (!s->callbacks.prepare_db) return NT_STATUS_OK;

	s->_pp.domain		= &s->domain;
	s->_pp.forest		= &s->forest;
	s->_pp.source_dsa	= &s->source_dsa;
	s->_pp.dest_dsa		= &s->dest_dsa;

	return s->callbacks.prepare_db(s->callbacks.private_data, &s->_pp);
}

static void becomeDC_drsuapi2_bind_recv(struct rpc_request *req);

static void becomeDC_drsuapi2_connect_recv(struct composite_context *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;

	c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi2.pipe);
	if (!composite_is_ok(c)) return;

	c->status = gensec_session_key(s->drsuapi2.pipe->conn->security_state.generic_state,
				       &s->drsuapi2.gensec_skey);
	if (!composite_is_ok(c)) return;

	becomeDC_drsuapi_bind_send(s, &s->drsuapi2, becomeDC_drsuapi2_bind_recv);
}

static void becomeDC_drsuapi3_connect_recv(struct composite_context *req);

static void becomeDC_drsuapi2_bind_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	char *binding_str;
	WERROR status;

	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsBind, &s->drsuapi2.bind_r);
	}

	status = becomeDC_drsuapi_bind_recv(s, &s->drsuapi2);
	if (!W_ERROR_IS_OK(status)) {
		composite_error(c, werror_to_ntstatus(status));
		return;
	}

	/* this avoids the epmapper lookup on the 3rd connection */
	binding_str = dcerpc_binding_string(s, s->drsuapi1.binding);
	if (composite_nomem(binding_str, c)) return;

	c->status = dcerpc_parse_binding(s, binding_str, &s->drsuapi3.binding);
	talloc_free(binding_str);
	if (!composite_is_ok(c)) return;

	/* w2k3 uses the same assoc_group_id as on the first connection, so we do */
	s->drsuapi3.binding->assoc_group_id	= s->drsuapi1.pipe->assoc_group_id;
	/* w2k3 uses the concurrent multiplex feature on the 3rd connection, so we do */
	s->drsuapi3.binding->flags		|= DCERPC_CONCURRENT_MULTIPLEX;

	becomeDC_drsuapi_connect_send(s, &s->drsuapi3, becomeDC_drsuapi3_connect_recv);
}

static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s);

static void becomeDC_drsuapi3_connect_recv(struct composite_context *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;

	c->status = dcerpc_pipe_connect_b_recv(req, s, &s->drsuapi3.pipe);
	if (!composite_is_ok(c)) return;

	c->status = gensec_session_key(s->drsuapi3.pipe->conn->security_state.generic_state,
				       &s->drsuapi3.gensec_skey);
	if (!composite_is_ok(c)) return;

	becomeDC_drsuapi3_pull_schema_send(s);
}

static void becomeDC_drsuapi_pull_partition_send(struct libnet_BecomeDC_state *s,
						 struct becomeDC_drsuapi *drsuapi_h,
						 struct becomeDC_drsuapi *drsuapi_p,
						 struct libnet_BecomeDC_Partition *partition,
						 void (*recv_fn)(struct rpc_request *req))
{
	struct composite_context *c = s->creq;
	struct rpc_request *req;
	struct drsuapi_DsGetNCChanges *r;

	r = talloc(s, struct drsuapi_DsGetNCChanges);
	if (composite_nomem(r, c)) return;

	r->out.level_out = talloc(r, int32_t);
	if (composite_nomem(r->out.level_out, c)) return;
	r->in.req = talloc(r, union drsuapi_DsGetNCChangesRequest);
	if (composite_nomem(r->in.req, c)) return;
	r->out.ctr = talloc(r, union drsuapi_DsGetNCChangesCtr);
	if (composite_nomem(r->out.ctr, c)) return;

	r->in.bind_handle	= &drsuapi_h->bind_handle;
	if (drsuapi_h->remote_info28.supported_extensions & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8) {
		r->in.level				= 8;
		r->in.req->req8.destination_dsa_guid	= partition->destination_dsa_guid;
		r->in.req->req8.source_dsa_invocation_id= partition->source_dsa_invocation_id;
		r->in.req->req8.naming_context		= &partition->nc;
		r->in.req->req8.highwatermark		= partition->highwatermark;
		r->in.req->req8.uptodateness_vector	= NULL;
		r->in.req->req8.replica_flags		= partition->replica_flags;
		r->in.req->req8.max_object_count	= 133;
		r->in.req->req8.max_ndr_size		= 1336811;
		r->in.req->req8.extended_op		= DRSUAPI_EXOP_NONE;
		r->in.req->req8.fsmo_info		= 0;
		r->in.req->req8.partial_attribute_set	= NULL;
		r->in.req->req8.partial_attribute_set_ex= NULL;
		r->in.req->req8.mapping_ctr.num_mappings= 0;
		r->in.req->req8.mapping_ctr.mappings	= NULL;
	} else {
		r->in.level				= 5;
		r->in.req->req5.destination_dsa_guid	= partition->destination_dsa_guid;
		r->in.req->req5.source_dsa_invocation_id= partition->source_dsa_invocation_id;
		r->in.req->req5.naming_context		= &partition->nc;
		r->in.req->req5.highwatermark		= partition->highwatermark;
		r->in.req->req5.uptodateness_vector	= NULL;
		r->in.req->req5.replica_flags		= partition->replica_flags;
		r->in.req->req5.max_object_count	= 133;
		r->in.req->req5.max_ndr_size		= 1336770;
		r->in.req->req5.extended_op		= DRSUAPI_EXOP_NONE;
		r->in.req->req5.fsmo_info		= 0;
	}

	/* 
	 * we should try to use the drsuapi_p->pipe here, as w2k3 does
	 * but it seems that some extra flags in the DCERPC Bind call
	 * are needed for it. Or the same KRB5 TGS is needed on both
	 * connections.
	 */
	req = dcerpc_drsuapi_DsGetNCChanges_send(drsuapi_p->pipe, r, r);
	composite_continue_rpc(c, req, recv_fn, s);
}

static WERROR becomeDC_drsuapi_pull_partition_recv(struct libnet_BecomeDC_state *s,
						   struct becomeDC_drsuapi *drsuapi_h,
						   struct becomeDC_drsuapi *drsuapi_p,
						   struct libnet_BecomeDC_Partition *partition,
						   struct drsuapi_DsGetNCChanges *r)
{
	uint32_t ctr_level = 0;
	struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
	struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
	struct GUID *source_dsa_guid = NULL;
	struct GUID *source_dsa_invocation_id = NULL;
	struct drsuapi_DsReplicaHighWaterMark *new_highwatermark = NULL;
	bool more_data = false;
	NTSTATUS nt_status;

	if (!W_ERROR_IS_OK(r->out.result)) {
		return r->out.result;
	}

	if (*r->out.level_out == 1) {
		ctr_level = 1;
		ctr1 = &r->out.ctr->ctr1;
	} else if (*r->out.level_out == 2 &&
		   r->out.ctr->ctr2.mszip1.ts) {
		ctr_level = 1;
		ctr1 = &r->out.ctr->ctr2.mszip1.ts->ctr1;
	} else if (*r->out.level_out == 6) {
		ctr_level = 6;
		ctr6 = &r->out.ctr->ctr6;
	} else if (*r->out.level_out == 7 &&
		   r->out.ctr->ctr7.level == 6 &&
		   r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP &&
		   r->out.ctr->ctr7.ctr.mszip6.ts) {
		ctr_level = 6;
		ctr6 = &r->out.ctr->ctr7.ctr.mszip6.ts->ctr6;
	} else if (*r->out.level_out == 7 &&
		   r->out.ctr->ctr7.level == 6 &&
		   r->out.ctr->ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS &&
		   r->out.ctr->ctr7.ctr.xpress6.ts) {
		ctr_level = 6;
		ctr6 = &r->out.ctr->ctr7.ctr.xpress6.ts->ctr6;
	} else {
		return WERR_BAD_NET_RESP;
	}

	if (!ctr1 && ! ctr6) {
		return WERR_BAD_NET_RESP;
	}

	if (ctr_level == 6) {
		if (!W_ERROR_IS_OK(ctr6->drs_error)) {
			return ctr6->drs_error;
		}
	}

	switch (ctr_level) {
	case 1:
		source_dsa_guid			= &ctr1->source_dsa_guid;
		source_dsa_invocation_id	= &ctr1->source_dsa_invocation_id;
		new_highwatermark		= &ctr1->new_highwatermark;
		more_data			= ctr1->more_data;
		break;
	case 6:
		source_dsa_guid			= &ctr6->source_dsa_guid;
		source_dsa_invocation_id	= &ctr6->source_dsa_invocation_id;
		new_highwatermark		= &ctr6->new_highwatermark;
		more_data			= ctr6->more_data;
		break;
	}

	partition->highwatermark		= *new_highwatermark;
	partition->source_dsa_guid		= *source_dsa_guid;
	partition->source_dsa_invocation_id	= *source_dsa_invocation_id;
	partition->more_data			= more_data;

	if (!partition->store_chunk) return WERR_OK;

	s->_sc.domain		= &s->domain;
	s->_sc.forest		= &s->forest;
	s->_sc.source_dsa	= &s->source_dsa;
	s->_sc.dest_dsa		= &s->dest_dsa;
	s->_sc.partition	= partition;
	s->_sc.ctr_level	= ctr_level;
	s->_sc.ctr1		= ctr1;
	s->_sc.ctr6		= ctr6;
	/* 
	 * we need to use the drsuapi_p->gensec_skey here,
	 * when we use drsuapi_p->pipe in the for this request
	 */
	s->_sc.gensec_skey	= &drsuapi_p->gensec_skey;

	nt_status = partition->store_chunk(s->callbacks.private_data, &s->_sc);
	if (!NT_STATUS_IS_OK(nt_status)) {
		return ntstatus_to_werror(nt_status);
	}

	return WERR_OK;
}

static void becomeDC_drsuapi3_pull_schema_recv(struct rpc_request *req);

static void becomeDC_drsuapi3_pull_schema_send(struct libnet_BecomeDC_state *s)
{
	s->schema_part.nc.guid	= GUID_zero();
	s->schema_part.nc.sid	= s->zero_sid;
	s->schema_part.nc.dn	= s->forest.schema_dn_str;

	s->schema_part.destination_dsa_guid	= s->drsuapi2.bind_guid;

	s->schema_part.replica_flags	= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;

	s->schema_part.store_chunk	= s->callbacks.schema_chunk;

	becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
					     becomeDC_drsuapi3_pull_schema_recv);
}

static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s);

static void becomeDC_drsuapi3_pull_schema_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsGetNCChanges);
	WERROR status;

	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
	}

	status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part, r);
	if (!W_ERROR_IS_OK(status)) {
		composite_error(c, werror_to_ntstatus(status));
		return;
	}

	talloc_free(r);

	if (s->schema_part.more_data) {
		becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->schema_part,
						     becomeDC_drsuapi3_pull_schema_recv);
		return;
	}

	becomeDC_drsuapi3_pull_config_send(s);
}

static void becomeDC_drsuapi3_pull_config_recv(struct rpc_request *req);

static void becomeDC_drsuapi3_pull_config_send(struct libnet_BecomeDC_state *s)
{
	s->config_part.nc.guid	= GUID_zero();
	s->config_part.nc.sid	= s->zero_sid;
	s->config_part.nc.dn	= s->forest.config_dn_str;

	s->config_part.destination_dsa_guid	= s->drsuapi2.bind_guid;

	s->config_part.replica_flags	= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;

	s->config_part.store_chunk	= s->callbacks.config_chunk;

	becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
					     becomeDC_drsuapi3_pull_config_recv);
}

static void becomeDC_drsuapi3_pull_config_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsGetNCChanges);
	WERROR status;

	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
	}

	status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->config_part, r);
	if (!W_ERROR_IS_OK(status)) {
		composite_error(c, werror_to_ntstatus(status));
		return;
	}

	talloc_free(r);

	if (s->config_part.more_data) {
		becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->config_part,
						     becomeDC_drsuapi3_pull_config_recv);
		return;
	}

	becomeDC_connect_ldap2(s);
}

static void becomeDC_drsuapi3_pull_domain_recv(struct rpc_request *req);

static void becomeDC_drsuapi3_pull_domain_send(struct libnet_BecomeDC_state *s)
{
	s->domain_part.nc.guid	= GUID_zero();
	s->domain_part.nc.sid	= s->zero_sid;
	s->domain_part.nc.dn	= s->domain.dn_str;

	s->domain_part.destination_dsa_guid	= s->drsuapi2.bind_guid;

	s->domain_part.replica_flags	= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_FULL_IN_PROGRESS
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
					| DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;

	s->domain_part.store_chunk	= s->callbacks.domain_chunk;

	becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
					     becomeDC_drsuapi3_pull_domain_recv);
}

static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
					      struct becomeDC_drsuapi *drsuapi,
					      struct libnet_BecomeDC_Partition *partition,
					      void (*recv_fn)(struct rpc_request *req));
static void becomeDC_drsuapi2_update_refs_schema_recv(struct rpc_request *req);

static void becomeDC_drsuapi3_pull_domain_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsGetNCChanges *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsGetNCChanges);
	WERROR status;
	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsGetNCChanges, r);
	}

	status = becomeDC_drsuapi_pull_partition_recv(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part, r);
	if (!W_ERROR_IS_OK(status)) {
		composite_error(c, werror_to_ntstatus(status));
		return;
	}

	talloc_free(r);

	if (s->domain_part.more_data) {
		becomeDC_drsuapi_pull_partition_send(s, &s->drsuapi2, &s->drsuapi3, &s->domain_part,
						     becomeDC_drsuapi3_pull_domain_recv);
		return;
	}

	becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->schema_part,
					  becomeDC_drsuapi2_update_refs_schema_recv);
}

static void becomeDC_drsuapi_update_refs_send(struct libnet_BecomeDC_state *s,
					      struct becomeDC_drsuapi *drsuapi,
					      struct libnet_BecomeDC_Partition *partition,
					      void (*recv_fn)(struct rpc_request *req))
{
	struct composite_context *c = s->creq;
	struct rpc_request *req;
	struct drsuapi_DsReplicaUpdateRefs *r;
	const char *ntds_guid_str;
	const char *ntds_dns_name;

	r = talloc(s, struct drsuapi_DsReplicaUpdateRefs);
	if (composite_nomem(r, c)) return;

	ntds_guid_str = GUID_string(r, &s->dest_dsa.ntds_guid);
	if (composite_nomem(ntds_guid_str, c)) return;

	ntds_dns_name = talloc_asprintf(r, "%s._msdcs.%s",
					ntds_guid_str,
					s->domain.dns_name);
	if (composite_nomem(ntds_dns_name, c)) return;

	r->in.bind_handle		= &drsuapi->bind_handle;
	r->in.level			= 1;
	r->in.req.req1.naming_context	= &partition->nc;
	r->in.req.req1.dest_dsa_dns_name= ntds_dns_name;
	r->in.req.req1.dest_dsa_guid	= s->dest_dsa.ntds_guid;
	r->in.req.req1.options		= DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
					| DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE;

	/* I think this is how we mark ourselves as a RODC */
	if (!lp_parm_bool(s->libnet->lp_ctx, NULL, "repl", "RODC", false)) {
		r->in.req.req1.options |= DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE;
	}

	req = dcerpc_drsuapi_DsReplicaUpdateRefs_send(drsuapi->pipe, r, r);
	composite_continue_rpc(c, req, recv_fn, s);
}

static void becomeDC_drsuapi2_update_refs_config_recv(struct rpc_request *req);

static void becomeDC_drsuapi2_update_refs_schema_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsReplicaUpdateRefs);
	bool print = false;

	if (req->p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
		print = true;
	}

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (print) {
		NDR_PRINT_OUT_DEBUG(drsuapi_DsReplicaUpdateRefs, r);
	}

	if (!W_ERROR_IS_OK(r->out.result)) {
		composite_error(c, werror_to_ntstatus(r->out.result));
		return;
	}

	talloc_free(r);

	becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->config_part,
					  becomeDC_drsuapi2_update_refs_config_recv);
}

static void becomeDC_drsuapi2_update_refs_domain_recv(struct rpc_request *req);

static void becomeDC_drsuapi2_update_refs_config_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsReplicaUpdateRefs);

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (!W_ERROR_IS_OK(r->out.result)) {
		composite_error(c, werror_to_ntstatus(r->out.result));
		return;
	}

	talloc_free(r);

	becomeDC_drsuapi_update_refs_send(s, &s->drsuapi2, &s->domain_part,
					  becomeDC_drsuapi2_update_refs_domain_recv);
}

static void becomeDC_drsuapi2_update_refs_domain_recv(struct rpc_request *req)
{
	struct libnet_BecomeDC_state *s = talloc_get_type(req->async.private_data,
					  struct libnet_BecomeDC_state);
	struct composite_context *c = s->creq;
	struct drsuapi_DsReplicaUpdateRefs *r = talloc_get_type(req->ndr.struct_ptr,
					   struct drsuapi_DsReplicaUpdateRefs);

	c->status = dcerpc_ndr_request_recv(req);
	if (!composite_is_ok(c)) return;

	if (!W_ERROR_IS_OK(r->out.result)) {
		composite_error(c, werror_to_ntstatus(r->out.result));
		return;
	}

	talloc_free(r);

	/* TODO: use DDNS updates and register dns names */
	composite_done(c);
}

static NTSTATUS becomeDC_ldap2_modify_computer(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_message *msg;
	uint32_t i;
	uint32_t user_account_control = UF_SERVER_TRUST_ACCOUNT |
					UF_TRUSTED_FOR_DELEGATION;

	/* as the value is already as we want it to be, we're done */
	if (s->dest_dsa.user_account_control == user_account_control) {
		return NT_STATUS_OK;
	}

	/* make a 'modify' msg, and only for serverReference */
	msg = ldb_msg_new(s);
	NT_STATUS_HAVE_NO_MEMORY(msg);
	msg->dn = ldb_dn_new(msg, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(msg->dn);

	ret = ldb_msg_add_fmt(msg, "userAccountControl", "%u", user_account_control);
	if (ret != 0) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}

	/* mark all the message elements (should be just one)
	   as LDB_FLAG_MOD_REPLACE */
	for (i=0;i<msg->num_elements;i++) {
		msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
	}

	ret = ldb_modify(s->ldap2.ldb, msg);
	talloc_free(msg);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	}

	s->dest_dsa.user_account_control = user_account_control;

	return NT_STATUS_OK;
}

static NTSTATUS becomeDC_ldap2_move_computer(struct libnet_BecomeDC_state *s)
{
	int ret;
	struct ldb_result *r;
	struct ldb_dn *basedn;
	struct ldb_dn *old_dn;
	struct ldb_dn *new_dn;
	static const char *_1_1_attrs[] = {
		"1.1",
		NULL
	};

	basedn = ldb_dn_new_fmt(s, s->ldap2.ldb, "<WKGUID=a361b2ffffd211d1aa4b00c04fd7d83a,%s>",
				s->domain.dn_str);
	NT_STATUS_HAVE_NO_MEMORY(basedn);

	ret = ldb_search(s->ldap2.ldb, s, &r, basedn, LDB_SCOPE_BASE,
			 _1_1_attrs, "(objectClass=*)");
	talloc_free(basedn);
	if (ret != LDB_SUCCESS) {
		return NT_STATUS_LDAP(ret);
	} else if (r->count != 1) {
		talloc_free(r);
		return NT_STATUS_INVALID_NETWORK_RESPONSE;
	}

	old_dn = ldb_dn_new(r, s->ldap2.ldb, s->dest_dsa.computer_dn_str);
	NT_STATUS_HAVE_NO_MEMORY(old_dn);

	new_dn = r->msgs[0]->dn;

	if (!ldb_dn_add_child_fmt(new_dn, "CN=%s", s->dest_dsa.netbios_name)) {
		talloc_free(r);
		return NT_STATUS_NO_MEMORY;
	}

	if (ldb_dn_compare(old_dn, new_dn) == 0) {
		/* we don't need to rename if the old and new dn match */
		talloc_free(r);
		return NT_STATUS_OK;
	}

	ret = ldb_rename(s->ldap2.ldb, old_dn, new_dn);
	if (ret != LDB_SUCCESS) {
		talloc_free(r);
		return NT_STATUS_LDAP(ret);
	}

	s->dest_dsa.computer_dn_str = ldb_dn_alloc_linearized(s, new_dn);
	NT_STATUS_HAVE_NO_MEMORY(s->dest_dsa.computer_dn_str);

	talloc_free(r);

	return NT_STATUS_OK;
}

static void becomeDC_connect_ldap2(struct libnet_BecomeDC_state *s)
{
	struct composite_context *c = s->creq;

	c->status = becomeDC_ldap_connect(s, &s->ldap2);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap2_modify_computer(s);
	if (!composite_is_ok(c)) return;

	c->status = becomeDC_ldap2_move_computer(s);
	if (!composite_is_ok(c)) return;

	becomeDC_drsuapi3_pull_domain_send(s);
}

struct composite_context *libnet_BecomeDC_send(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
{
	struct composite_context *c;
	struct libnet_BecomeDC_state *s;
	char *tmp_name;

	c = composite_create(mem_ctx, ctx->event_ctx);
	if (c == NULL) return NULL;

	s = talloc_zero(c, struct libnet_BecomeDC_state);
	if (composite_nomem(s, c)) return c;
	c->private_data = s;
	s->creq		= c;
	s->libnet	= ctx;

	/* Domain input */
	s->domain.dns_name	= talloc_strdup(s, r->in.domain_dns_name);
	if (composite_nomem(s->domain.dns_name, c)) return c;
	s->domain.netbios_name	= talloc_strdup(s, r->in.domain_netbios_name);
	if (composite_nomem(s->domain.netbios_name, c)) return c;
	s->domain.sid		= dom_sid_dup(s, r->in.domain_sid);
	if (composite_nomem(s->domain.sid, c)) return c;

	/* Source DSA input */
	s->source_dsa.address	= talloc_strdup(s, r->in.source_dsa_address);
	if (composite_nomem(s->source_dsa.address, c)) return c;

	/* Destination DSA input */
	s->dest_dsa.netbios_name= talloc_strdup(s, r->in.dest_dsa_netbios_name);
	if (composite_nomem(s->dest_dsa.netbios_name, c)) return c;

	/* Destination DSA dns_name construction */
	tmp_name	= strlower_talloc(s, s->dest_dsa.netbios_name);
	if (composite_nomem(tmp_name, c)) return c;
	tmp_name	= talloc_asprintf_append_buffer(tmp_name, ".%s",s->domain.dns_name);
	if (composite_nomem(tmp_name, c)) return c;
	s->dest_dsa.dns_name	= tmp_name;

	/* Callback function pointers */
	s->callbacks = r->in.callbacks;

	becomeDC_send_cldap(s);
	return c;
}

NTSTATUS libnet_BecomeDC_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
{
	NTSTATUS status;

	status = composite_wait(c);

	ZERO_STRUCT(r->out);

	talloc_free(c);
	return status;
}

NTSTATUS libnet_BecomeDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_BecomeDC *r)
{
	NTSTATUS status;
	struct composite_context *c;
	c = libnet_BecomeDC_send(ctx, mem_ctx, r);
	status = libnet_BecomeDC_recv(c, mem_ctx, r);
	return status;
}
