diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index a2af071a..3c4ceeb5 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -1638,7 +1638,6 @@ static void
 multi_client_connect_post(struct multi_context *m,
                           struct multi_instance *mi,
                           const char *dc_file,
-                          unsigned int option_permissions_mask,
                           unsigned int *option_types_found)
 {
     /* Did script generate a dynamic config file? */
@@ -1647,7 +1646,7 @@ multi_client_connect_post(struct multi_context *m,
         options_server_import(&mi->context.options,
                               dc_file,
                               D_IMPORT_ERRORS|M_OPTERR,
-                              option_permissions_mask,
+                              CLIENT_CONNECT_OPT_MASK,
                               option_types_found,
                               mi->context.c2.es);
 
@@ -1671,7 +1670,6 @@ static void
 multi_client_connect_post_plugin(struct multi_context *m,
                                  struct multi_instance *mi,
                                  const struct plugin_return *pr,
-                                 unsigned int option_permissions_mask,
                                  unsigned int *option_types_found)
 {
     struct plugin_return config;
@@ -1689,7 +1687,7 @@ multi_client_connect_post_plugin(struct multi_context *m,
                 options_string_import(&mi->context.options,
                                       config.list[i]->value,
                                       D_IMPORT_ERRORS|M_OPTERR,
-                                      option_permissions_mask,
+                                      CLIENT_CONNECT_OPT_MASK,
                                       option_types_found,
                                       mi->context.c2.es);
             }
@@ -1716,7 +1714,6 @@ multi_client_connect_post_plugin(struct multi_context *m,
 static void
 multi_client_connect_mda(struct multi_context *m,
                          struct multi_instance *mi,
-                         unsigned int option_permissions_mask,
                          unsigned int *option_types_found)
 {
     if (mi->cc_config)
@@ -1729,7 +1726,7 @@ multi_client_connect_mda(struct multi_context *m,
             options_string_import(&mi->context.options,
                                   opt,
                                   D_IMPORT_ERRORS|M_OPTERR,
-                                  option_permissions_mask,
+                                  CLIENT_CONNECT_OPT_MASK,
                                   option_types_found,
                                   mi->context.c2.es);
         }
@@ -1843,160 +1840,46 @@ multi_client_set_protocol_options(struct context *c)
     }
 }
 
-/**
- * Generates the data channel keys
- */
-static bool
-multi_client_generate_tls_keys(struct context *c)
-{
-    struct frame *frame_fragment = NULL;
-#ifdef ENABLE_FRAGMENT
-    if (c->options.ce.fragment)
-    {
-        frame_fragment = &c->c2.frame_fragment;
-    }
-#endif
-    struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
-    if (!tls_session_update_crypto_params(session, &c->options,
-                                          &c->c2.frame, frame_fragment))
-    {
-        msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed");
-        register_signal(c, SIGUSR1, "process-push-msg-failed");
-        return false;
-    }
-
-    return true;
-}
-
-/*
- * Called as soon as the SSL/TLS connection authenticates.
- *
- * Instance-specific directives to be processed:
- *
- *   iroute start-ip end-ip
- *   ifconfig-push local remote-netmask
- *   push
- */
 static void
-multi_connection_established(struct multi_context *m, struct multi_instance *mi)
+multi_client_connect_call_plugin_v1(struct multi_context *m,
+                                    struct multi_instance *mi,
+                                    unsigned int *option_types_found,
+                                    int *cc_succeeded,
+                                    int *cc_succeeded_count)
 {
-    if (tls_authentication_status(mi->context.c2.tls_multi, 0) != TLS_AUTHENTICATION_SUCCEEDED)
-    {
-        return;
-    }
-
-    struct gc_arena gc = gc_new();
-    unsigned int option_types_found = 0;
-
-    const unsigned int option_permissions_mask =
-        OPT_P_INSTANCE
-        | OPT_P_INHERIT
-        | OPT_P_PUSH
-        | OPT_P_TIMER
-        | OPT_P_CONFIG
-        | OPT_P_ECHO
-        | OPT_P_COMP
-        | OPT_P_SOCKFLAGS;
-
-    int cc_succeeded = true;     /* client connect script status */
-    int cc_succeeded_count = 0;
-
-    ASSERT(mi->context.c1.tuntap);
-
-    /* lock down the common name and cert hashes so they can't change during future TLS renegotiations */
-    tls_lock_common_name(mi->context.c2.tls_multi);
-    tls_lock_cert_hash_set(mi->context.c2.tls_multi);
-
-    /* generate a msg() prefix for this client instance */
-    generate_prefix(mi);
-
-    /* delete instances of previous clients with same common-name */
-    if (!mi->context.options.duplicate_cn)
-    {
-        multi_delete_dup(m, mi);
-    }
-
-    /* reset pool handle to null */
-    mi->vaddr_handle = -1;
-
-    /*
-     * Try to source a dynamic config file from the
-     * --client-config-dir directory.
-     */
-    if (mi->context.options.client_config_dir)
-    {
-        const char *ccd_file;
-
-        ccd_file = platform_gen_path(mi->context.options.client_config_dir,
-                                     tls_common_name(mi->context.c2.tls_multi,
-                                                     false),
-                                     &gc);
-
-        /* try common-name file */
-        if (platform_test_file(ccd_file))
-        {
-            options_server_import(&mi->context.options,
-                                  ccd_file,
-                                  D_IMPORT_ERRORS|M_OPTERR,
-                                  option_permissions_mask,
-                                  &option_types_found,
-                                  mi->context.c2.es);
-        }
-        else     /* try default file */
-        {
-            ccd_file = platform_gen_path(mi->context.options.client_config_dir,
-                                         CCD_DEFAULT,
-                                         &gc);
-
-            if (platform_test_file(ccd_file))
-            {
-                options_server_import(&mi->context.options,
-                                      ccd_file,
-                                      D_IMPORT_ERRORS|M_OPTERR,
-                                      option_permissions_mask,
-                                      &option_types_found,
-                                      mi->context.c2.es);
-            }
-        }
-    }
-
-    /*
-     * Select a virtual address from either --ifconfig-push in --client-config-dir file
-     * or --ifconfig-pool.
-     */
-    multi_select_virtual_addr(m, mi);
-
-    /* do --client-connect setenvs */
-    multi_client_connect_setenv(m, mi);
-
 #ifdef ENABLE_PLUGIN
-    /*
-     * Call client-connect plug-in.
-     */
+    ASSERT(m);
+    ASSERT(mi);
+    ASSERT(option_types_found);
+    ASSERT(cc_succeeded);
+    ASSERT(cc_succeeded_count);
 
     /* deprecated callback, use a file for passing back return info */
     if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT))
     {
         struct argv argv = argv_new();
-        const char *dc_file = platform_create_temp_file(mi->context.options.tmp_dir,
-                                                        "cc", &gc);
+        struct gc_arena gc = gc_new();
+        const char *dc_file =
+            platform_create_temp_file(mi->context.options.tmp_dir, "cc", &gc);
 
         if (!dc_file)
         {
             cc_succeeded = false;
-            goto script_depr_failed;
+            goto cleanup;
         }
 
         argv_printf(&argv, "%s", dc_file);
-        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT, &argv, NULL, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT,
+                        &argv, NULL, mi->context.c2.es)
+            != OPENVPN_PLUGIN_FUNC_SUCCESS)
         {
             msg(M_WARN, "WARNING: client-connect plugin call failed");
-            cc_succeeded = false;
+            *cc_succeeded = false;
         }
         else
         {
-            multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found);
-            ++cc_succeeded_count;
+            multi_client_connect_post(m, mi, dc_file, option_types_found);
+            (*cc_succeeded_count)++;
         }
 
         if (!platform_unlink(dc_file))
@@ -2005,9 +1888,26 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)
                 dc_file);
         }
 
-script_depr_failed:
+cleanup:
         argv_free(&argv);
+        gc_free(&gc);
     }
+#endif /* ifdef ENABLE_PLUGIN */
+}
+
+static void
+multi_client_connect_call_plugin_v2(struct multi_context *m,
+                                    struct multi_instance *mi,
+                                    unsigned int *option_types_found,
+                                    int *cc_succeeded,
+                                    int *cc_succeeded_count)
+{
+#ifdef ENABLE_PLUGIN
+    ASSERT(m);
+    ASSERT(mi);
+    ASSERT(option_types_found);
+    ASSERT(cc_succeeded);
+    ASSERT(cc_succeeded_count);
 
     /* V2 callback, use a plugin_return struct for passing back return info */
     if (plugin_defined(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2))
@@ -2016,27 +1916,42 @@ script_depr_failed:
 
         plugin_return_init(&pr);
 
-        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2, NULL, &pr, mi->context.c2.es) != OPENVPN_PLUGIN_FUNC_SUCCESS)
+        if (plugin_call(mi->context.plugins, OPENVPN_PLUGIN_CLIENT_CONNECT_V2,
+                        NULL, &pr, mi->context.c2.es)
+            != OPENVPN_PLUGIN_FUNC_SUCCESS)
         {
             msg(M_WARN, "WARNING: client-connect-v2 plugin call failed");
-            cc_succeeded = false;
+            *cc_succeeded = false;
         }
         else
         {
-            multi_client_connect_post_plugin(m, mi, &pr, option_permissions_mask, &option_types_found);
-            ++cc_succeeded_count;
+            multi_client_connect_post_plugin(m, mi, &pr, option_types_found);
+            (*cc_succeeded_count)++;
         }
 
         plugin_return_free(&pr);
     }
 #endif /* ifdef ENABLE_PLUGIN */
+}
 
-    /*
-     * Run --client-connect script.
-     */
-    if (mi->context.options.client_connect_script && cc_succeeded)
+
+
+/**
+ * Runs the --client-connect script if one is defined.
+ */
+static void
+multi_client_connect_call_script(struct multi_context *m,
+                                 struct multi_instance *mi,
+                                 unsigned int *option_types_found,
+                                 int *cc_succeeded,
+                                 int *cc_succeeded_count)
+{
+    ASSERT(m);
+    ASSERT(mi);
+    if (mi->context.options.client_connect_script)
     {
         struct argv argv = argv_new();
+        struct gc_arena gc = gc_new();
         const char *dc_file = NULL;
 
         setenv_str(mi->context.c2.es, "script_type", "client-connect");
@@ -2045,8 +1960,8 @@ script_depr_failed:
                                             "cc", &gc);
         if (!dc_file)
         {
-            cc_succeeded = false;
-            goto script_failed;
+            *cc_succeeded = false;
+            goto cleanup;
         }
 
         argv_parse_cmd(&argv, mi->context.options.client_connect_script);
@@ -2054,12 +1969,12 @@ script_depr_failed:
 
         if (openvpn_run_script(&argv, mi->context.c2.es, 0, "--client-connect"))
         {
-            multi_client_connect_post(m, mi, dc_file, option_permissions_mask, &option_types_found);
-            ++cc_succeeded_count;
+            multi_client_connect_post(m, mi, dc_file, option_types_found);
+            (*cc_succeeded_count)++;
         }
         else
         {
-            cc_succeeded = false;
+            *cc_succeeded = false;
         }
 
         if (!platform_unlink(dc_file))
@@ -2067,130 +1982,322 @@ script_depr_failed:
             msg(D_MULTI_ERRORS, "MULTI: problem deleting temporary file: %s",
                 dc_file);
         }
-
-script_failed:
+cleanup:
         argv_free(&argv);
+        gc_free(&gc);
     }
+}
 
-    /*
-     * Check for client-connect script left by management interface client
-     */
-#ifdef MANAGEMENT_DEF_AUTH
-    if (cc_succeeded && mi->cc_config)
+/**
+ * Generates the data channel keys
+ */
+static bool
+multi_client_generate_tls_keys(struct context *c)
+{
+  struct frame *frame_fragment = NULL;
+#ifdef ENABLE_FRAGMENT
+  if (c->options.ce.fragment)
     {
-        multi_client_connect_mda(m, mi, option_permissions_mask, &option_types_found);
-        ++cc_succeeded_count;
+      frame_fragment = &c->c2.frame_fragment;
     }
 #endif
+  struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
+  if (!tls_session_update_crypto_params(session, &c->options,
+                                        &c->c2.frame, frame_fragment))
+    {
+      msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed");
+      register_signal(c, SIGUSR1, "process-push-msg-failed");
+      return false;
+    }
 
+  return true;
+}
+
+static void
+multi_client_connect_late_setup(struct multi_context *m,
+                                struct multi_instance *mi,
+                                const unsigned int option_types_found)
+{
+    ASSERT(m);
+    ASSERT(mi);
+
+    struct gc_arena gc = gc_new();
     /*
-     * Check for "disable" directive in client-config-dir file
-     * or config file generated by --client-connect script.
+     * Process sourced options.
      */
-    if (mi->context.options.disable)
+    do_deferred_options(&mi->context, option_types_found);
+
+    /*
+     * make sure we got ifconfig settings from somewhere
+     */
+    if (!mi->context.c2.push_ifconfig_defined)
     {
-        msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to 'disable' directive");
-        cc_succeeded = false;
-        cc_succeeded_count = 0;
+        msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote"
+            "--ifconfig address is available for %s",
+            multi_instance_string(mi, false, &gc));
     }
 
-    if (cc_succeeded)
+    /*
+     * make sure that ifconfig settings comply with constraints
+     */
+    if (!ifconfig_push_constraint_satisfied(&mi->context))
     {
-        /*
-         * Process sourced options.
-         */
-        do_deferred_options(&mi->context, option_types_found);
+        const char *ifconfig_constraint_network =
+            print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc);
+        const char *ifconfig_constraint_netmask =
+            print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc);
 
-        /*
-         * make sure we got ifconfig settings from somewhere
-         */
-        if (!mi->context.c2.push_ifconfig_defined)
+        /* JYFIXME -- this should cause the connection to fail */
+        msg(D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s)"
+            "violates tunnel network/netmask constraint (%s/%s)",
+            multi_instance_string(mi, false, &gc),
+            print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc),
+            ifconfig_constraint_network, ifconfig_constraint_netmask);
+    }
+
+    /*
+     * For routed tunnels, set up internal route to endpoint
+     * plus add all iroute routes.
+     */
+    if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
+    {
+        if (mi->context.c2.push_ifconfig_defined)
         {
-            msg(D_MULTI_ERRORS, "MULTI: no dynamic or static remote --ifconfig address is available for %s",
-                multi_instance_string(mi, false, &gc));
+            multi_learn_in_addr_t(m, mi,
+                                  mi->context.c2.push_ifconfig_local,
+                                  -1, true);
+            msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
+                multi_instance_string(mi, false, &gc),
+                print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc));
         }
 
-        /*
-         * make sure that ifconfig settings comply with constraints
-         */
-        if (!ifconfig_push_constraint_satisfied(&mi->context))
+        if (mi->context.c2.push_ifconfig_ipv6_defined)
         {
-            /* JYFIXME -- this should cause the connection to fail */
-            msg(D_MULTI_ERRORS, "MULTI ERROR: primary virtual IP for %s (%s) violates tunnel network/netmask constraint (%s/%s)",
+            multi_learn_in6_addr(m, mi,
+                                 mi->context.c2.push_ifconfig_ipv6_local,
+                                 -1, true);
+            /* TODO: find out where addresses are "unlearned"!! */
+            const char *ifconfig_local_ipv6 =
+                print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc);
+            msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
                 multi_instance_string(mi, false, &gc),
-                print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc),
-                print_in_addr_t(mi->context.options.push_ifconfig_constraint_network, 0, &gc),
-                print_in_addr_t(mi->context.options.push_ifconfig_constraint_netmask, 0, &gc));
+                ifconfig_local_ipv6);
         }
 
+        /* add routes locally, pointing to new client, if
+         * --iroute options have been specified */
+        multi_add_iroutes(m, mi);
+
         /*
-         * For routed tunnels, set up internal route to endpoint
-         * plus add all iroute routes.
+         * iroutes represent subnets which are "owned" by a particular
+         * client.  Therefore, do not actually push a route to a client
+         * if it matches one of the client's iroutes.
          */
-        if (TUNNEL_TYPE(mi->context.c1.tuntap) == DEV_TYPE_TUN)
-        {
-            if (mi->context.c2.push_ifconfig_defined)
-            {
-                multi_learn_in_addr_t(m, mi, mi->context.c2.push_ifconfig_local, -1, true);
-                msg(D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
-                    multi_instance_string(mi, false, &gc),
-                    print_in_addr_t(mi->context.c2.push_ifconfig_local, 0, &gc));
-            }
+        remove_iroutes_from_push_route_list(&mi->context.options);
+    }
+    else if (mi->context.options.iroutes)
+    {
+        msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute"
+            "only works with tun-style tunnels",
+            multi_instance_string(mi, false, &gc));
+    }
 
-            if (mi->context.c2.push_ifconfig_ipv6_defined)
-            {
-                multi_learn_in6_addr(m, mi, mi->context.c2.push_ifconfig_ipv6_local, -1, true);
-                /* TODO: find out where addresses are "unlearned"!! */
-                msg(D_MULTI_LOW, "MULTI: primary virtual IPv6 for %s: %s",
-                    multi_instance_string(mi, false, &gc),
-                    print_in6_addr(mi->context.c2.push_ifconfig_ipv6_local, 0, &gc));
-            }
+    /* set our client's VPN endpoint for status reporting purposes */
+    mi->reporting_addr = mi->context.c2.push_ifconfig_local;
+    mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local;
 
-            /* add routes locally, pointing to new client, if
-             * --iroute options have been specified */
-            multi_add_iroutes(m, mi);
+    /* set context-level authentication flag */
+    mi->context.c2.context_auth = CAS_SUCCEEDED;
 
-            /*
-             * iroutes represent subnets which are "owned" by a particular
-             * client.  Therefore, do not actually push a route to a client
-             * if it matches one of the client's iroutes.
-             */
-            remove_iroutes_from_push_route_list(&mi->context.options);
-        }
-        else if (mi->context.options.iroutes)
-        {
-            msg(D_MULTI_ERRORS, "MULTI: --iroute options rejected for %s -- iroute only works with tun-style tunnels",
-                multi_instance_string(mi, false, &gc));
-        }
+    /* authentication complete, calculate dynamic client specific options */
+    multi_client_set_protocol_options(&mi->context);
+
+    /* Generate data channel keys */
+    if (!multi_client_generate_tls_keys(&mi->context))
+    {
+        mi->context.c2.context_auth = CAS_FAILED;
+    }
 
-        /* set our client's VPN endpoint for status reporting purposes */
-        mi->reporting_addr = mi->context.c2.push_ifconfig_local;
-        mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local;
+    /* send push reply if ready */
+    if (mi->context.c2.push_request_received)
+    {
+        process_incoming_push_request(&mi->context);
+    }
 
-        /* set context-level authentication flag */
-        mi->context.c2.context_auth = CAS_SUCCEEDED;
+    gc_free(&gc);
+}
+
+static void
+multi_client_connect_early_setup(struct multi_context *m,
+                                 struct multi_instance *mi)
+{
+    ASSERT(mi->context.c1.tuntap);
+    /*
+     * lock down the common name and cert hashes so they can't change
+     * during future TLS renegotiations
+     */
+    tls_lock_common_name(mi->context.c2.tls_multi);
+    tls_lock_cert_hash_set(mi->context.c2.tls_multi);
+
+    /* generate a msg() prefix for this client instance */
+    generate_prefix(mi);
+
+    /* delete instances of previous clients with same common-name */
+    if (!mi->context.options.duplicate_cn)
+    {
+        multi_delete_dup(m, mi);
+    }
+
+    /* reset pool handle to null */
+    mi->vaddr_handle = -1;
+}
+
+/**
+ * Try to source a dynamic config file from the
+ * --client-config-dir directory.
+ */
+static void
+multi_client_connect_source_ccd(struct multi_context *m,
+                                struct multi_instance *mi,
+                                unsigned int *option_types_found)
+{
+    if (mi->context.options.client_config_dir)
+    {
+        struct gc_arena gc = gc_new();
+        const char *ccd_file;
 
-        /* authentication complete, calculate dynamic client specific options */
-        multi_client_set_protocol_options(&mi->context);
+        ccd_file = platform_gen_path(mi->context.options.client_config_dir,
+                                     tls_common_name(mi->context.c2.tls_multi,
+                                                     false),
+                                     &gc);
 
-        /* Generate data channel keys */
-        if (!multi_client_generate_tls_keys(&mi->context))
+        /* try common-name file */
+        if (platform_test_file(ccd_file))
         {
-            mi->context.c2.context_auth = CAS_FAILED;
+            options_server_import(&mi->context.options,
+                                  ccd_file,
+                                  D_IMPORT_ERRORS|M_OPTERR,
+                                  CLIENT_CONNECT_OPT_MASK,
+                                  option_types_found,
+                                  mi->context.c2.es);
         }
-
-        /* send push reply if ready */
-        if (mi->context.c2.push_request_received)
+        else /* try default file */
         {
-            process_incoming_push_request(&mi->context);
+            ccd_file = platform_gen_path(mi->context.options.client_config_dir,
+                                         CCD_DEFAULT,
+                                         &gc);
+
+            if (platform_test_file(ccd_file))
+            {
+                options_server_import(&mi->context.options,
+                                      ccd_file,
+                                      D_IMPORT_ERRORS|M_OPTERR,
+                                      CLIENT_CONNECT_OPT_MASK,
+                                      option_types_found,
+                                      mi->context.c2.es);
+            }
         }
+        gc_free(&gc);
+    }
+}
+
+/*
+ * Called as soon as the SSL/TLS connection is authenticated.
+ *
+ * Will collect the client specific configuration from the different
+ * sources like ccd files, connect plugins and management interface.
+ *
+ * This method starts with cas_context CAS_PENDING and will move the
+ * state machine to either CAS_SUCCEEDED on success or
+ * CAS_FAILED/CAS_PARTIAL on failure.
+ *
+ * Instance-specific directives to be processed (CLIENT_CONNECT_OPT_MASK)
+ * include:
+ *
+ *   iroute start-ip end-ip
+ *   ifconfig-push local remote-netmask
+ *   push
+ *
+ *
+ */
+static void
+multi_connection_established(struct multi_context *m, struct multi_instance *mi)
+{
+    if (tls_authentication_status(mi->context.c2.tls_multi, 0)
+        != TLS_AUTHENTICATION_SUCCEEDED)
+    {
+        return;
+    }
+    unsigned int option_types_found = 0;
+
+    int cc_succeeded = true;     /* client connect script status */
+    int cc_succeeded_count = 0;
+
+    multi_client_connect_early_setup(m, mi);
+
+    multi_client_connect_source_ccd(m, mi, &option_types_found);
+
+    /*
+     * Select a virtual address from either --ifconfig-push in
+     * --client-config-dir file or --ifconfig-pool.
+     */
+    multi_select_virtual_addr(m, mi);
+
+    /* do --client-connect setenvs */
+    multi_client_connect_setenv(m, mi);
+
+    multi_client_connect_call_plugin_v1(m, mi, &option_types_found,
+                                        &cc_succeeded,
+                                        &cc_succeeded_count);
+
+    multi_client_connect_call_plugin_v2(m, mi, &option_types_found,
+                                        &cc_succeeded,
+                                        &cc_succeeded_count);
+
+    /*
+     * Check for client-connect script left by management interface client
+     */
+    if (cc_succeeded)
+    {
+        multi_client_connect_call_script(m, mi, &option_types_found,
+                                         &cc_succeeded,
+                                         &cc_succeeded_count);
+    }
+
+#ifdef MANAGEMENT_DEF_AUTH
+    if (cc_succeeded && mi->cc_config)
+    {
+        multi_client_connect_mda(m, mi, &option_types_found);
+        ++cc_succeeded_count;
+    }
+#endif
+
+    /*
+     * Check for "disable" directive in client-config-dir file
+     * or config file generated by --client-connect script.
+     */
+    if (mi->context.options.disable)
+    {
+        msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to "
+            " 'disable' directive");
+        cc_succeeded = false;
+        cc_succeeded_count = 0;
+    }
+
+
+
+    if (cc_succeeded)
+    {
+        multi_client_connect_late_setup(m, mi, option_types_found);
     }
     else
     {
         /* set context-level authentication flag */
-        mi->context.c2.context_auth = cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
+        mi->context.c2.context_auth =
+            cc_succeeded_count ? CAS_PARTIAL : CAS_FAILED;
     }
 
+
     /* increment number of current authenticated clients */
     ++m->n_clients;
     update_mstat_n_clients(m->n_clients);
@@ -2199,11 +2306,10 @@ script_failed:
 #ifdef MANAGEMENT_DEF_AUTH
     if (management)
     {
-        management_connection_established(management, &mi->context.c2.mda_context, mi->context.c2.es);
+        management_connection_established(management,
+                                          &mi->context.c2.mda_context, mi->context.c2.es);
     }
 #endif
-
-    gc_free(&gc);
 }
 
 #ifdef ENABLE_ASYNC_PUSH
diff --git a/src/openvpn/multi.h b/src/openvpn/multi.h
index 8c9c4609..c51107f4 100644
--- a/src/openvpn/multi.h
+++ b/src/openvpn/multi.h
@@ -623,7 +623,9 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flags
     return ret;
 }
 
-
+#define CLIENT_CONNECT_OPT_MASK (OPT_P_INSTANCE | OPT_P_INHERIT   \
+                                 |OPT_P_PUSH | OPT_P_TIMER | OPT_P_CONFIG   \
+                                 |OPT_P_ECHO | OPT_P_COMP | OPT_P_SOCKFLAGS)
 
 static inline bool
 multi_process_outgoing_link_dowork(struct multi_context *m, struct multi_instance *mi, const unsigned int mpp_flags)
