r17472: Remove unused variable.
[samba.git] / source4 / libnet / libnet_rpc.c
index 3e9f069fb575af318869defc0c6b2221bea3501d..eedef7989d30c01ce77cb9fba1543dfb771a1005 100644 (file)
 #include "includes.h"
 #include "libnet/libnet.h"
 #include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "librpc/rpc/dcerpc.h"
 #include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+
+
+struct rpc_connect_srv_state {
+       struct libnet_context *ctx;
+       struct libnet_RpcConnect r;
+       const char *binding;
+};
+
+
+static void continue_pipe_connect(struct composite_context *ctx);
+
 
 /**
- * Connects rpc pipe on remote server
+ * Initiates connection to rpc pipe on remote server
  * 
  * @param ctx initialised libnet context
  * @param mem_ctx memory context of this call
  * @param r data structure containing necessary parameters and return values
- * @return nt status of the call
+ * @return composite context of this call
  **/
 
-static NTSTATUS libnet_RpcConnectSrv(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+static struct composite_context* libnet_RpcConnectSrv_send(struct libnet_context *ctx,
+                                                          TALLOC_CTX *mem_ctx,
+                                                          struct libnet_RpcConnect *r)
 {
-       NTSTATUS status;
-       const char *binding = NULL;
+       struct composite_context *c;    
+       struct rpc_connect_srv_state *s;
+       struct composite_context *pipe_connect_req;
+
+       /* composite context allocation and setup */
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
+
+       s = talloc_zero(c, struct rpc_connect_srv_state);
+       if (composite_nomem(s, c)) return c;
+
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = s;
+       c->event_ctx = ctx->event_ctx;
+
+       s->ctx = ctx;
+       s->r = *r;
+       ZERO_STRUCT(s->r.out);
 
        /* prepare binding string */
        switch (r->level) {
        case LIBNET_RPC_CONNECT_DC:
        case LIBNET_RPC_CONNECT_PDC:
        case LIBNET_RPC_CONNECT_SERVER:
-               binding = talloc_asprintf(mem_ctx, "ncacn_np:%s", r->in.name);
+               s->binding = talloc_asprintf(s, "ncacn_np:%s", r->in.name);
                break;
 
        case LIBNET_RPC_CONNECT_BINDING:
-               binding = r->in.binding;
+               s->binding = talloc_strdup(s, r->in.binding);
                break;
+
+       case LIBNET_RPC_CONNECT_DC_INFO:
+               /* this should never happen - DC_INFO level has a separate
+                  composite function */
+               composite_error(c, NT_STATUS_INVALID_LEVEL);
+               return c;
        }
 
        /* connect to remote dcerpc pipe */
-       status = dcerpc_pipe_connect(mem_ctx, &r->out.dcerpc_pipe,
-                                    binding, r->in.dcerpc_iface,
-                                    ctx->cred, ctx->event_ctx);
-
-       if (!NT_STATUS_IS_OK(status)) {
-               r->out.error_string = talloc_asprintf(mem_ctx,
-                                                     "dcerpc_pipe_connect to pipe %s[%s] failed with %s\n",
-                                                     r->in.dcerpc_iface->name, binding, nt_errstr(status));
-               return status;
-       }
+       pipe_connect_req = dcerpc_pipe_connect_send(c, &s->r.out.dcerpc_pipe,
+                                                   s->binding, r->in.dcerpc_iface,
+                                                   ctx->cred, c->event_ctx);
+       if (composite_nomem(pipe_connect_req, c)) return c;
 
-       r->out.error_string = NULL;
-       ctx->pipe = r->out.dcerpc_pipe;
+       composite_continue(c, pipe_connect_req, continue_pipe_connect, c);
+       return c;
+}
 
-       return status;
+
+/*
+  Step 2 of RpcConnectSrv - get rpc connection
+*/
+static void continue_pipe_connect(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct rpc_connect_srv_state *s;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+
+       /* receive result of rpc pipe connection */
+       c->status = dcerpc_pipe_connect_recv(ctx, c, &s->r.out.dcerpc_pipe);
+
+       s->r.out.error_string = NULL;
+       composite_done(c);
 }
 
 
 /**
- * Connects rpc pipe on domain pdc
- * 
+ * Receives result of connection to rpc pipe on remote server
+ *
+ * @param c composite context
  * @param ctx initialised libnet context
  * @param mem_ctx memory context of this call
  * @param r data structure containing necessary parameters and return values
- * @return nt status of the call
+ * @return nt status of rpc connection
  **/
 
-static NTSTATUS libnet_RpcConnectDC(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+static NTSTATUS libnet_RpcConnectSrv_recv(struct composite_context *c,
+                                         struct libnet_context *ctx,
+                                         TALLOC_CTX *mem_ctx,
+                                         struct libnet_RpcConnect *r)
 {
        NTSTATUS status;
+       struct rpc_connect_srv_state *s = talloc_get_type(c->private_data,
+                                         struct rpc_connect_srv_state);
+
+       status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               /* move the returned rpc pipe between memory contexts */
+               s = talloc_get_type(c->private_data, struct rpc_connect_srv_state);
+               r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+               } else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
+                       ctx->lsa_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               }
+       } else {
+               r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
+       }
+
+       talloc_free(c);
+       return status;
+}
+
+
+struct rpc_connect_dc_state {
+       struct libnet_context *ctx;
+       struct libnet_RpcConnect r;
        struct libnet_RpcConnect r2;
        struct libnet_LookupDCs f;
        const char *connect_name;
 
-       f.in.domain_name  = r->in.name;
+       /* information about the progress */
+       void (*monitor_fn)(struct monitor_msg *);
+};
+
+
+static void continue_lookup_dc(struct composite_context *ctx);
+static void continue_rpc_connect(struct composite_context *ctx);
+
+
+/**
+ * Initiates connection to rpc pipe on domain pdc
+ * 
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+static struct composite_context* libnet_RpcConnectDC_send(struct libnet_context *ctx,
+                                                         TALLOC_CTX *mem_ctx,
+                                                         struct libnet_RpcConnect *r)
+{
+       struct composite_context *c;
+       struct rpc_connect_dc_state *s;
+       struct composite_context *lookup_dc_req;
+
+       /* composite context allocation and setup */
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
+
+       s = talloc_zero(c, struct rpc_connect_dc_state);
+       if (composite_nomem(s, c)) return c;
+
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = s;
+       c->event_ctx = ctx->event_ctx;
+
+       s->ctx = ctx;
+       s->r   = *r;
+       ZERO_STRUCT(s->r.out);
+
        switch (r->level) {
        case LIBNET_RPC_CONNECT_PDC:
-               f.in.name_type = NBT_NAME_PDC;
+               s->f.in.name_type = NBT_NAME_PDC;
                break;
+
        case LIBNET_RPC_CONNECT_DC:
-               f.in.name_type = NBT_NAME_LOGON;
+               s->f.in.name_type = NBT_NAME_LOGON;
                break;
+
        default:
                break;
        }
-       f.out.num_dcs = 0;
-       f.out.dcs  = NULL;
+
+       s->f.in.domain_name = r->in.name;
+       s->f.out.num_dcs    = 0;
+       s->f.out.dcs        = NULL;
 
        /* find the domain pdc first */
-       status = libnet_LookupDCs(ctx, mem_ctx, &f);
-       if (!NT_STATUS_IS_OK(status)) {
-               r->out.error_string = talloc_asprintf(mem_ctx, "libnet_LookupDCs failed: %s",
-                                                     nt_errstr(status));
-               return status;
-       }
+       lookup_dc_req = libnet_LookupDCs_send(ctx, c, &s->f);
+       if (composite_nomem(lookup_dc_req, c)) return c;
 
-       /* we might not have got back a name.  Fall back to the IP */
-       if (f.out.dcs[0].name) {
-               connect_name = f.out.dcs[0].name;
-       } else {
-               connect_name = f.out.dcs[0].address;
+       composite_continue(c, lookup_dc_req, continue_lookup_dc, c);
+       return c;
+}
+
+
+/*
+  Step 2 of RpcConnectDC: get domain controller name/address and
+  initiate RpcConnect to it
+*/
+static void continue_lookup_dc(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct rpc_connect_dc_state *s;
+       struct composite_context *rpc_connect_req;
+       struct monitor_msg msg;
+       struct msg_net_lookup_dc data;
+       
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
+       
+       /* receive result of domain controller lookup */
+       c->status = libnet_LookupDCs_recv(ctx, c, &s->f);
+       if (!composite_is_ok(c)) return;
+
+       /* decide on preferred address type depending on DC type */
+       switch (s->r.level) {
+       case LIBNET_RPC_CONNECT_PDC:
+               s->connect_name = s->f.out.dcs[0].name;
+               break;
+
+       case LIBNET_RPC_CONNECT_DC:
+               s->connect_name = s->f.out.dcs[0].address;
+               break;
+
+       default:
+               /* we shouldn't absolutely get here */
+               composite_error(c, NT_STATUS_INVALID_LEVEL);
        }
 
+       /* prepare a monitor message and post it */
+       msg.type         = net_lookup_dc;
+       msg.data         = &data;
+       msg.data_size    = sizeof(data);
+
+       data.domain_name = s->f.in.domain_name;
+       data.hostname    = s->f.out.dcs[0].name;
+       data.address     = s->f.out.dcs[0].address;
+       
+       if (s->monitor_fn) s->monitor_fn(&msg);
+
        /* ok, pdc has been found so do attempt to rpc connect */
-       r2.level            = LIBNET_RPC_CONNECT_SERVER;
+       s->r2.level            = s->r.level;
 
-       /* This will cause yet another name resolution, but at least
+       /* this will cause yet another name resolution, but at least
         * we pass the right name down the stack now */
-       r2.in.name          = talloc_strdup(mem_ctx, connect_name);
-       r2.in.dcerpc_iface  = r->in.dcerpc_iface;
-       
-       status = libnet_RpcConnect(ctx, mem_ctx, &r2);
+       s->r2.in.name          = talloc_strdup(c, s->connect_name);
+       s->r2.in.dcerpc_iface  = s->r.in.dcerpc_iface;  
 
-       r->out.dcerpc_pipe          = r2.out.dcerpc_pipe;
-       r->out.error_string         = r2.out.error_string;
+       /* send rpc connect request to the server */
+       rpc_connect_req = libnet_RpcConnectSrv_send(s->ctx, c, &s->r2);
+       if (composite_nomem(rpc_connect_req, c)) return;
 
-       ctx->pipe = r->out.dcerpc_pipe;
+       composite_continue(c, rpc_connect_req, continue_rpc_connect, c);
+}
 
-       return status;
+
+/*
+  Step 3 of RpcConnectDC: get rpc connection to the server
+*/
+static void continue_rpc_connect(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct rpc_connect_dc_state *s;
+       struct monitor_msg msg;
+       struct msg_net_pipe_connected data;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dc_state);
+
+       c->status = libnet_RpcConnectSrv_recv(ctx, s->ctx, c, &s->r2);
+
+       /* error string is to be passed anyway */
+       s->r.out.error_string  = s->r2.out.error_string;
+       if (!composite_is_ok(c)) return;
+
+       s->r.out.dcerpc_pipe = s->r2.out.dcerpc_pipe;
+
+       /* prepare a monitor message and post it */
+       msg.type       = net_pipe_connected;
+       msg.data       = NULL;
+       msg.data_size  = 0;
+
+       data.host      = s->r.out.dcerpc_pipe->binding->host;
+       data.endpoint  = s->r.out.dcerpc_pipe->binding->endpoint;
+       data.transport = s->r.out.dcerpc_pipe->binding->transport;
+
+       if (s->monitor_fn) s->monitor_fn(&msg);
+
+       composite_done(c);
 }
 
 
 /**
- * Connects to rpc pipe on remote server or pdc
- * 
+ * Receives result of connection to rpc pipe on domain pdc
+ *
+ * @param c composite context
  * @param ctx initialised libnet context
  * @param mem_ctx memory context of this call
  * @param r data structure containing necessary parameters and return values
- * @return nt status of the call
+ * @return nt status of rpc connection
  **/
 
-NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+static NTSTATUS libnet_RpcConnectDC_recv(struct composite_context *c,
+                                        struct libnet_context *ctx,
+                                        TALLOC_CTX *mem_ctx,
+                                        struct libnet_RpcConnect *r)
 {
-       switch (r->level) {
-               case LIBNET_RPC_CONNECT_SERVER:
-                       return libnet_RpcConnectSrv(ctx, mem_ctx, r);
+       NTSTATUS status;
+       struct rpc_connect_dc_state *s = talloc_get_type(c->private_data,
+                                        struct rpc_connect_dc_state);
 
-               case LIBNET_RPC_CONNECT_BINDING:
-                       return libnet_RpcConnectSrv(ctx, mem_ctx, r);
+       status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               /* move connected rpc pipe between memory contexts */
+               r->out.dcerpc_pipe = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
 
-               case LIBNET_RPC_CONNECT_PDC:
-               case LIBNET_RPC_CONNECT_DC:
-                       return libnet_RpcConnectDC(ctx, mem_ctx, r);
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+               } else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
+                       ctx->lsa_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+               }
+
+       } else {
+               r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
        }
 
-       return NT_STATUS_INVALID_LEVEL;
+       talloc_free(c);
+       return status;
 }
 
+
+
+struct rpc_connect_dci_state {
+       struct libnet_context *ctx;
+       struct libnet_RpcConnect r;
+       struct libnet_RpcConnect rpc_conn;
+       struct policy_handle lsa_handle;
+       struct lsa_QosInfo qos;
+       struct lsa_ObjectAttribute attr;
+       struct lsa_OpenPolicy2 lsa_open_policy;
+       struct dcerpc_pipe *lsa_pipe;
+       struct lsa_QueryInfoPolicy2 lsa_query_info2;
+       struct lsa_QueryInfoPolicy lsa_query_info;
+       struct dcerpc_binding *final_binding;
+       struct dcerpc_pipe *final_pipe;
+};
+
+
+static void continue_dci_rpc_connect(struct composite_context *ctx);
+static void continue_lsa_policy(struct rpc_request *req);
+static void continue_lsa_query_info(struct rpc_request *req);
+static void continue_lsa_query_info2(struct rpc_request *req);
+static void continue_epm_map_binding(struct composite_context *ctx);
+static void continue_secondary_conn(struct composite_context *ctx);
+
+
 /**
- * Connects to rpc pipe on remote server or pdc, and returns info on the domain name, domain sid and realm
+ * Initiates connection to rpc pipe on remote server or pdc. Received result
+ * contains info on the domain name, domain sid and realm.
  * 
  * @param ctx initialised libnet context
- * @param r data structure containing necessary parameters and return values.  Must be a talloc context
- * @return nt status of the call
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values. Must be a talloc context
+ * @return composite context of this call
  **/
 
-NTSTATUS libnet_RpcConnectDCInfo(struct libnet_context *ctx, 
-                                struct libnet_RpcConnectDCInfo *r)
+static struct composite_context* libnet_RpcConnectDCInfo_send(struct libnet_context *ctx,
+                                                             TALLOC_CTX *mem_ctx,
+                                                             struct libnet_RpcConnect *r)
 {
-       TALLOC_CTX *tmp_ctx;
-       NTSTATUS status;
+       struct composite_context *c, *conn_req;
+       struct rpc_connect_dci_state *s;
 
-       struct libnet_RpcConnect *c;
-       struct lsa_ObjectAttribute attr;
-       struct lsa_QosInfo qos;
-       struct lsa_OpenPolicy2 lsa_open_policy;
-       struct policy_handle lsa_p_handle;
-       struct lsa_QueryInfoPolicy2 lsa_query_info2;
-       struct lsa_QueryInfoPolicy lsa_query_info;
+       /* composite context allocation and setup */
+       c = talloc_zero(mem_ctx, struct composite_context);
+       if (c == NULL) return NULL;
 
-       struct dcerpc_pipe *lsa_pipe;
+       s = talloc_zero(c, struct rpc_connect_dci_state);
+       if (composite_nomem(s, c)) return c;
 
-       struct dcerpc_binding *final_binding;
-       struct dcerpc_pipe *final_pipe;
+       c->state = COMPOSITE_STATE_IN_PROGRESS;
+       c->private_data = s;
+       c->event_ctx = ctx->event_ctx;
 
-       tmp_ctx = talloc_new(r);
-       if (!tmp_ctx) {
-               r->out.error_string = NULL;
-               return NT_STATUS_NO_MEMORY;
+       s->ctx = ctx;
+       s->r   = *r;
+       ZERO_STRUCT(s->r.out);
+
+       /* proceed to pure rpc connection if the binding string is provided,
+          otherwise try to connect domain controller */
+       if (r->in.binding == NULL) {
+               s->rpc_conn.in.name    = r->in.name;
+               s->rpc_conn.level      = LIBNET_RPC_CONNECT_DC;
+       } else {
+               s->rpc_conn.in.binding = r->in.binding;
+               s->rpc_conn.level      = LIBNET_RPC_CONNECT_BINDING;
        }
+
+       /* we need to query information on lsarpc interface first */
+       s->rpc_conn.in.dcerpc_iface    = &dcerpc_table_lsarpc;
        
-       c = talloc(tmp_ctx, struct libnet_RpcConnect);
-       if (!c) {
-               r->out.error_string = NULL;
-               talloc_free(tmp_ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
-       c->level              = r->level;
+       /* request connection to the lsa pipe on the pdc */
+       conn_req = libnet_RpcConnect_send(ctx, c, &s->rpc_conn);
+       if (composite_nomem(c, conn_req)) return c;
 
-       if (r->level != LIBNET_RPC_CONNECT_BINDING) {
-               c->in.name    = r->in.name;
-       } else {
-               c->in.binding = r->in.binding;
+       composite_continue(c, conn_req, continue_dci_rpc_connect, c);
+       return c;
+}
+
+
+/*
+  Step 2 of RpcConnectDCInfo: receive opened rpc pipe and open
+  lsa policy handle
+*/
+static void continue_dci_rpc_connect(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct rpc_connect_dci_state *s;
+       struct rpc_request *open_pol_req;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = libnet_RpcConnect_recv(ctx, s->ctx, c, &s->rpc_conn);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
        }
+
+       /* prepare to open a policy handle on lsa pipe */
+       s->lsa_pipe = s->ctx->lsa_pipe;
        
-       c->in.dcerpc_iface    = &dcerpc_table_lsarpc;
+       s->qos.len                 = 0;
+       s->qos.impersonation_level = 2;
+       s->qos.context_mode        = 1;
+       s->qos.effective_only      = 0;
+
+       s->attr.sec_qos = &s->qos;
+
+       s->lsa_open_policy.in.attr        = &s->attr;
+       s->lsa_open_policy.in.system_name = talloc_asprintf(c, "\\");
+       if (composite_nomem(s->lsa_open_policy.in.system_name, c)) return;
+
+       s->lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       s->lsa_open_policy.out.handle     = &s->lsa_handle;
+
+       open_pol_req = dcerpc_lsa_OpenPolicy2_send(s->lsa_pipe, c, &s->lsa_open_policy);
+       if (composite_nomem(open_pol_req, c)) return;
+
+       composite_continue_rpc(c, open_pol_req, continue_lsa_policy, c);
+}
+
+
+/*
+  Step 3 of RpcConnectDCInfo: Get policy handle and query lsa info
+  for kerberos realm (dns name) and guid. The query may fail.
+*/
+static void continue_lsa_policy(struct rpc_request *req)
+{
+       struct composite_context *c;
+       struct rpc_connect_dci_state *s;
+       struct rpc_request *query_info_req;
+
+       c = talloc_get_type(req->async.private, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               composite_error(c, c->status);
+               return;
+       }
+
+       if (!NT_STATUS_IS_OK(s->lsa_query_info2.out.result)) {
+               composite_error(c, s->lsa_query_info2.out.result);
+               return;
+       }
+
+       /* query lsa info for dns domain name and guid */
+       s->lsa_query_info2.in.handle = &s->lsa_handle;
+       s->lsa_query_info2.in.level  = LSA_POLICY_INFO_DNS;
+
+       query_info_req = dcerpc_lsa_QueryInfoPolicy2_send(s->lsa_pipe, c, &s->lsa_query_info2);
+       if (composite_nomem(query_info_req, c)) return;
+
+       composite_continue_rpc(c, query_info_req, continue_lsa_query_info2, c);
+}
+
+
+/*
+  Step 4 of RpcConnectDCInfo: Get realm and guid if provided (rpc call
+  may result in failure) and query lsa info for domain name and sid.
+*/
+static void continue_lsa_query_info2(struct rpc_request *req)
+{      
+       struct composite_context *c;
+       struct rpc_connect_dci_state *s;
+       struct rpc_request *query_info_req;
+
+       c = talloc_get_type(req->async.private, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = dcerpc_ndr_request_recv(req);
        
-       /* connect to the LSA pipe of the PDC */
-
-       status = libnet_RpcConnect(ctx, c, c);
-       if (!NT_STATUS_IS_OK(status)) {
-               if (r->level != LIBNET_RPC_CONNECT_BINDING) {
-                       r->out.error_string = talloc_asprintf(r,
-                                                             "Connection to LSA pipe of DC failed: %s",
-                                                             c->out.error_string);
-               } else {
-                       r->out.error_string = talloc_asprintf(r,
-                                                             "Connection to LSA pipe with binding '%s' failed: %s",
-                                                             r->in.binding, c->out.error_string);
+       /* In case of error just null the realm and guid and proceed
+          to the next step. After all, it doesn't have to be AD domain
+          controller we talking to - NT-style PDC also counts */
+
+       if (NT_STATUS_EQUAL(c->status, NT_STATUS_NET_WRITE_FAULT)) {
+               s->r.out.realm = NULL;
+               s->r.out.guid  = NULL;
+
+       } else {
+               if (!NT_STATUS_IS_OK(c->status)) {
+                       s->r.out.error_string = talloc_asprintf(c,
+                                                               "lsa_QueryInfoPolicy2 failed: %s",
+                                                               nt_errstr(c->status));
+                       composite_error(c, c->status);
+                       return;
                }
-               talloc_free(tmp_ctx);
-               return status;
-       }                       
-       lsa_pipe = c->out.dcerpc_pipe;
-       
-       /* Get an LSA policy handle */
-
-       ZERO_STRUCT(lsa_p_handle);
-       qos.len = 0;
-       qos.impersonation_level = 2;
-       qos.context_mode = 1;
-       qos.effective_only = 0;
-
-       attr.len = 0;
-       attr.root_dir = NULL;
-       attr.object_name = NULL;
-       attr.attributes = 0;
-       attr.sec_desc = NULL;
-       attr.sec_qos = &qos;
-
-       lsa_open_policy.in.attr = &attr;
+
+               if (!NT_STATUS_IS_OK(s->lsa_query_info2.out.result)) {
+                       s->r.out.error_string = talloc_asprintf(c,
+                                                               "lsa_QueryInfoPolicy2 failed: %s",
+                                                               nt_errstr(s->lsa_query_info2.out.result));
+                       composite_error(c, s->lsa_query_info2.out.result);
+                       return;
+               }
+
+               /* Copy the dns domain name and guid from the query result */
+
+               /* this should actually be a conversion from lsa_StringLarge */
+               s->r.out.realm = s->lsa_query_info2.out.info->dns.dns_domain.string;
+               s->r.out.guid  = talloc(c, struct GUID);
+               if (composite_nomem(s->r.out.guid, c)) {
+                       s->r.out.error_string = NULL;
+                       return;
+               }
+               *s->r.out.guid = s->lsa_query_info2.out.info->dns.domain_guid;
+       }
+
+       /* query lsa info for domain name and sid */
+       s->lsa_query_info.in.handle = &s->lsa_handle;
+       s->lsa_query_info.in.level  = LSA_POLICY_INFO_DOMAIN;
+
+       query_info_req = dcerpc_lsa_QueryInfoPolicy_send(s->lsa_pipe, c, &s->lsa_query_info);
+       if (composite_nomem(query_info_req, c)) return;
+
+       composite_continue_rpc(c, query_info_req, continue_lsa_query_info, c);
+}
+
+
+/*
+  Step 5 of RpcConnectDCInfo: Get domain name and sid and request endpoint
+  map binding
+*/
+static void continue_lsa_query_info(struct rpc_request *req)
+{
+       struct composite_context *c, *epm_map_req;
+       struct rpc_connect_dci_state *s;
+
+       c = talloc_get_type(req->async.private, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = dcerpc_ndr_request_recv(req);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               s->r.out.error_string = talloc_asprintf(c,
+                                                       "lsa_QueryInfoPolicy failed: %s",
+                                                       nt_errstr(c->status));
+               composite_error(c, c->status);
+               return;
+       }
+
+       /* Copy the domain name and sid from the query result */
+       s->r.out.domain_sid  = s->lsa_query_info.out.info->domain.sid;
+       s->r.out.domain_name = s->lsa_query_info.out.info->domain.name.string;
+
+
+       /* prepare to get endpoint mapping for the requested interface */
+       s->final_binding = talloc(s, struct dcerpc_binding);
+       if (composite_nomem(s->final_binding, c)) return;
        
-       lsa_open_policy.in.system_name = talloc_asprintf(tmp_ctx, "\\"); 
-       if (!lsa_open_policy.in.system_name) {
-               r->out.error_string = NULL;
-               talloc_free(tmp_ctx);
-               return NT_STATUS_NO_MEMORY;
+       *s->final_binding = *s->lsa_pipe->binding;
+       /* Ensure we keep hold of the member elements */
+       talloc_reference(s->final_binding, s->lsa_pipe->binding);
+
+       epm_map_req = dcerpc_epm_map_binding_send(c, s->final_binding, s->r.in.dcerpc_iface,
+                                                 s->lsa_pipe->conn->event_ctx);
+       if (composite_nomem(epm_map_req, c)) return;
+
+       composite_continue(c, epm_map_req, continue_epm_map_binding, c);
+}
+
+
+/*
+  Step 6 of RpcConnectDCInfo: Receive endpoint mapping and create secondary
+  rpc connection derived from already used pipe but connected to the requested
+  one (as specified in libnet_RpcConnect structure)
+*/
+static void continue_epm_map_binding(struct composite_context *ctx)
+{
+       struct composite_context *c, *sec_conn_req;
+       struct rpc_connect_dci_state *s;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = dcerpc_epm_map_binding_recv(ctx);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               s->r.out.error_string = talloc_asprintf(c,
+                                                       "failed to map pipe with endpoint mapper - %s",
+                                                       nt_errstr(c->status));
+               composite_error(c, c->status);
+               return;
        }
 
-       lsa_open_policy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
-       lsa_open_policy.out.handle = &lsa_p_handle;
+       /* create secondary connection derived from lsa pipe */
+       sec_conn_req = dcerpc_secondary_connection_send(s->lsa_pipe, s->final_binding);
+       if (composite_nomem(sec_conn_req, c)) return;
 
-       status = dcerpc_lsa_OpenPolicy2(lsa_pipe, tmp_ctx, &lsa_open_policy); 
+       composite_continue(c, sec_conn_req, continue_secondary_conn, c);
+}
 
-       /* This now fails on ncacn_ip_tcp against Win2k3 SP1 */
-       if (NT_STATUS_IS_OK(status)) {
-               /* Look to see if this is ADS (a fault indicates NT4 or Samba 3.0) */
-               
-               lsa_query_info2.in.handle = &lsa_p_handle;
-               lsa_query_info2.in.level = LSA_POLICY_INFO_DNS;
-               
-               status = dcerpc_lsa_QueryInfoPolicy2(lsa_pipe, tmp_ctx,                 
-                                                    &lsa_query_info2);
-               
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
-                       if (!NT_STATUS_IS_OK(status)) {
-                               r->out.error_string = talloc_asprintf(r,
-                                                                     "lsa_QueryInfoPolicy2 failed: %s",
-                                                                     nt_errstr(status));
-                               talloc_free(tmp_ctx);
-                               return status;
-                       }
-                       r->out.realm = lsa_query_info2.out.info->dns.dns_domain.string;
-                       r->out.guid = talloc(r, struct GUID);
-                       if (!r->out.guid) {
-                               r->out.error_string = NULL;
-                               return NT_STATUS_NO_MEMORY;
-                       }
-                       *r->out.guid = lsa_query_info2.out.info->dns.domain_guid;
-               } else {
-                       r->out.realm = NULL;
-                       r->out.guid = NULL;
-               }
-               
-               /* Grab the domain SID (regardless of the result of the previous call */
-               
-               lsa_query_info.in.handle = &lsa_p_handle;
-               lsa_query_info.in.level = LSA_POLICY_INFO_DOMAIN;
-               
-               status = dcerpc_lsa_QueryInfoPolicy(lsa_pipe, tmp_ctx, 
-                                                   &lsa_query_info);
+
+/*
+  Step 7 of RpcConnectDCInfo: Get actual lsa pipe to be returned
+  and complete this composite call
+*/
+static void continue_secondary_conn(struct composite_context *ctx)
+{
+       struct composite_context *c;
+       struct rpc_connect_dci_state *s;
+
+       c = talloc_get_type(ctx->async.private_data, struct composite_context);
+       s = talloc_get_type(c->private_data, struct rpc_connect_dci_state);
+
+       c->status = dcerpc_secondary_connection_recv(ctx, &s->final_pipe);
+       if (!NT_STATUS_IS_OK(c->status)) {
+               s->r.out.error_string = talloc_asprintf(c,
+                                                       "secondary connection failed: %s",
+                                                       nt_errstr(c->status));
                
-               if (!NT_STATUS_IS_OK(status)) {
-                       r->out.error_string = talloc_asprintf(r,
-                                                             "lsa_QueryInfoPolicy2 failed: %s",
-                                                             nt_errstr(status));
-                       talloc_free(tmp_ctx);
-                       return status;
+               composite_error(c, c->status);
+               return;
+       }
+
+       s->r.out.dcerpc_pipe = s->final_pipe;
+       composite_done(c);
+}
+
+
+/**
+ * Receives result of connection to rpc pipe and gets basic
+ * domain info (name, sid, realm, guid)
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing return values
+ * @return nt status of rpc connection
+ **/
+
+static NTSTATUS libnet_RpcConnectDCInfo_recv(struct composite_context *c, struct libnet_context *ctx,
+                                            TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+       NTSTATUS status;
+       struct rpc_connect_dci_state *s = talloc_get_type(c->private_data,
+                                         struct rpc_connect_dci_state);
+
+       status = composite_wait(c);
+       if (NT_STATUS_IS_OK(status)) {
+               r->out.realm        = talloc_steal(mem_ctx, s->r.out.realm);
+               r->out.guid         = talloc_steal(mem_ctx, s->r.out.guid);
+               r->out.domain_name  = talloc_steal(mem_ctx, s->r.out.domain_name);
+               r->out.domain_sid   = talloc_steal(mem_ctx, s->r.out.domain_sid);
+
+               r->out.dcerpc_pipe  = talloc_steal(mem_ctx, s->r.out.dcerpc_pipe);
+
+               /* reference created pipe structure to long-term libnet_context
+                  so that it can be used by other api functions even after short-term
+                  mem_ctx is freed */
+               if (r->in.dcerpc_iface == &dcerpc_table_samr) {
+                       ctx->samr_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
+
+               } else if (r->in.dcerpc_iface == &dcerpc_table_lsarpc) {
+                       ctx->lsa_pipe = talloc_reference(ctx, r->out.dcerpc_pipe);
                }
-               
-               r->out.domain_sid = lsa_query_info.out.info->domain.sid;
-               r->out.domain_name = lsa_query_info.out.info->domain.name.string;
+
        } else {
-               /* Cause the code further down to try this with just SAMR */
-               r->out.domain_sid = NULL;
-               r->out.domain_name = NULL;
-               r->out.realm = NULL;
+               r->out.error_string = talloc_steal(mem_ctx, s->r.out.error_string);
        }
 
-       /* Find the original binding string */
-       final_binding = talloc(tmp_ctx, struct dcerpc_binding);
-       if (!final_binding) {
-               return NT_STATUS_NO_MEMORY;
-       }
-       *final_binding = *lsa_pipe->binding;
-       /* Ensure we keep hold of the member elements */
-       talloc_reference(final_binding, lsa_pipe->binding);
-
-       /* Make binding string for samr, not the other pipe */
-       status = dcerpc_epm_map_binding(tmp_ctx, final_binding,                                         
-                                       r->in.dcerpc_iface,
-                                       lsa_pipe->conn->event_ctx);
-       if (!NT_STATUS_IS_OK(status)) {
-               r->out.error_string = talloc_asprintf(r,
-                                                     "Failed to map pipe with endpoint mapper - %s",
-                                                     nt_errstr(status));
-               talloc_free(tmp_ctx);
-               return status;
-       }
+       talloc_free(c);
+       return status;
+}
+
+
+/**
+ * Initiates connection to rpc pipe on remote server or pdc, optionally
+ * providing domain info
+ * 
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return composite context of this call
+ **/
+
+struct composite_context* libnet_RpcConnect_send(struct libnet_context *ctx,
+                                                TALLOC_CTX *mem_ctx,
+                                                struct libnet_RpcConnect *r)
+{
+       struct composite_context *c;
+
+       switch (r->level) {
+       case LIBNET_RPC_CONNECT_SERVER:
+       case LIBNET_RPC_CONNECT_BINDING:
+               c = libnet_RpcConnectSrv_send(ctx, mem_ctx, r);
+               break;
+
+       case LIBNET_RPC_CONNECT_PDC:
+       case LIBNET_RPC_CONNECT_DC:
+               c = libnet_RpcConnectDC_send(ctx, mem_ctx, r);
+               break;
 
-       /* Now that we have the info setup a final connection to the pipe they wanted */
-       status = dcerpc_secondary_connection(lsa_pipe, &final_pipe, final_binding);
-       if (!NT_STATUS_IS_OK(status)) {
-               r->out.error_string = talloc_asprintf(r,
-                                                     "secondary connection failed: %s",
-                                                     nt_errstr(status));
-               talloc_free(tmp_ctx);
-               return status;
+       case LIBNET_RPC_CONNECT_DC_INFO:
+               c = libnet_RpcConnectDCInfo_send(ctx, mem_ctx, r);
+               break;
+
+       default:
+               c = talloc_zero(mem_ctx, struct composite_context);
+               composite_error(c, NT_STATUS_INVALID_LEVEL);
        }
-       r->out.dcerpc_pipe = final_pipe;
 
-       talloc_steal(r, r->out.realm);
-       talloc_steal(r, r->out.domain_sid);
-       talloc_steal(r, r->out.domain_name);
-       talloc_steal(r, r->out.dcerpc_pipe);
+       return c;
+}
+
+
+/**
+ * Receives result of connection to rpc pipe on remote server or pdc
+ *
+ * @param c composite context
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect_recv(struct composite_context *c, struct libnet_context *ctx,
+                               TALLOC_CTX *mem_ctx, struct libnet_RpcConnect *r)
+{
+       switch (r->level) {
+       case LIBNET_RPC_CONNECT_SERVER:
+       case LIBNET_RPC_CONNECT_BINDING:
+               return libnet_RpcConnectSrv_recv(c, ctx, mem_ctx, r);
+
+       case LIBNET_RPC_CONNECT_PDC:
+       case LIBNET_RPC_CONNECT_DC:
+               return libnet_RpcConnectDC_recv(c, ctx, mem_ctx, r);
+
+       case LIBNET_RPC_CONNECT_DC_INFO:
+               return libnet_RpcConnectDCInfo_recv(c, ctx, mem_ctx, r);
 
-       /* This should close the LSA pipe, which we don't need now we have the info */
-       talloc_free(tmp_ctx);
-       return NT_STATUS_OK;
+       default:
+               ZERO_STRUCT(r->out);
+               return NT_STATUS_INVALID_LEVEL;
+       }
 }
 
+
+/**
+ * Connect to a rpc pipe on a remote server - sync version
+ *
+ * @param ctx initialised libnet context
+ * @param mem_ctx memory context of this call
+ * @param r data structure containing necessary parameters and return values
+ * @return nt status of rpc connection
+ **/
+
+NTSTATUS libnet_RpcConnect(struct libnet_context *ctx, TALLOC_CTX *mem_ctx,
+                          struct libnet_RpcConnect *r)
+{
+       struct composite_context *c;
+       
+       c = libnet_RpcConnect_send(ctx, mem_ctx, r);
+       return libnet_RpcConnect_recv(c, ctx, mem_ctx, r);
+}