]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Windows reliability changes:
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Mon, 31 Oct 2005 03:01:17 +0000 (03:01 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Mon, 31 Oct 2005 03:01:17 +0000 (03:01 +0000)
* Added code to make sure that the local PATH environmental
variable points to the Windows system32 directory.
* Added new --ip-win32 adaptive mode which tries 'dynamic'
and then fails over to 'netsh' if the DHCP negotiation fails.
* Made --ip-win32 adaptive the default.

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@739 e7ae566f-a301-0410-adde-c780ea21d3b5

12 files changed:
errlevel.h
forward.c
init.c
misc.c
misc.h
options.c
ping.c
plugin.h
route.c
ssl.c
tun.c
tun.h

index d7db6479a9c8e7acf4092baa58846ad7d23bb36c..cbe833673e451daed656ab9549f4ff36577d0bc6 100644 (file)
 #define D_SHOW_PKCS11        LOGLEV(7, 70, M_DEBUG)  /* show PKCS#11 actions */
 #define D_ALIGN_DEBUG        LOGLEV(7, 70, M_DEBUG)  /* show verbose struct alignment info */
 #define D_PACKET_TRUNC_DEBUG LOGLEV(7, 70, M_DEBUG)  /* PACKET_TRUNCATION_CHECK verbose */
+#define D_PING               LOGLEV(7, 70, M_DEBUG)  /* PING send/receive messages */
 
 #define D_HANDSHAKE_VERBOSE  LOGLEV(8, 70, M_DEBUG)  /* show detailed description of each handshake */
 #define D_TLS_DEBUG_MED      LOGLEV(8, 70, M_DEBUG)  /* limited info from tls_session routines */
index ae2122fed85b659b1054fb5b123e2139f78a4d32..338d8624b67bac705e1370df15a77916aa88238a 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -274,8 +274,12 @@ check_add_routes_dowork (struct context *c)
   else
     {
       msg (D_ROUTE, "Route: Waiting for TUN/TAP interface to come up...");
+      if (c->c1.tuntap)
+       tun_standby (c->c1.tuntap);
+      update_time ();
       if (c->c2.route_wakeup.n != 1)
        event_timeout_init (&c->c2.route_wakeup, 1, now);
+      event_timeout_reset (&c->c2.ping_rec_interval);
     }
 }
 
@@ -773,7 +777,7 @@ process_incoming_link (struct context *c)
 #endif
 
 #ifdef PACKET_TRUNCATION_CHECK
-      /* if (c->c2.buf.len > 1) --c->c2.buf.len; JYFIXME */
+      /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
       ipv4_packet_size_verify (BPTR (&c->c2.buf),
                               BLEN (&c->c2.buf),
                               TUNNEL_TYPE (c->c1.tuntap),
@@ -807,7 +811,7 @@ process_incoming_link (struct context *c)
       /* Did we just receive an openvpn ping packet? */
       if (is_ping_msg (&c->c2.buf))
        {
-         dmsg (D_PACKET_CONTENT, "RECEIVED PING PACKET");
+         dmsg (D_PING, "RECEIVED PING PACKET");
          c->c2.buf.len = 0; /* drop packet */
        }
 
@@ -911,7 +915,7 @@ process_incoming_tun (struct context *c)
       process_ipv4_header (c, PIPV4_PASSTOS|PIPV4_MSSFIX, &c->c2.buf);
 
 #ifdef PACKET_TRUNCATION_CHECK
-      /* if (c->c2.buf.len > 1) --c->c2.buf.len; JYFIXME */
+      /* if (c->c2.buf.len > 1) --c->c2.buf.len; */
       ipv4_packet_size_verify (BPTR (&c->c2.buf),
                               BLEN (&c->c2.buf),
                               TUNNEL_TYPE (c->c1.tuntap),
diff --git a/init.c b/init.c
index fa57a33c0f54b854ba5d09a51987c37a4b216e7d..5f1a9bb5e346a975baa2d16172e1a78d25a3c653 100644 (file)
--- a/init.c
+++ b/init.c
@@ -122,7 +122,7 @@ context_init_1 (struct context *c)
   }
 #endif
 
-#if 0 /* JYFIXME -- test get_user_pass with GET_USER_PASS_NEED_OK flag */
+#if 0 /* test get_user_pass with GET_USER_PASS_NEED_OK flag */
  {
    /*
     * In the management interface, you can okay the request by entering "needok token-insertion-request ok"
@@ -175,6 +175,8 @@ context_gc_free (struct context *c)
 bool
 init_static (void)
 {
+  configure_path ();
+
 #if defined(USE_CRYPTO) && defined(DMALLOC)
   openssl_dmalloc_init ();
 #endif
@@ -964,6 +966,8 @@ do_up (struct context *c, bool pulled_options, unsigned int option_types_found)
            {
              event_timeout_init (&c->c2.route_wakeup, c->options.route_delay, now);
              event_timeout_init (&c->c2.route_wakeup_expire, c->options.route_delay + c->options.route_delay_window, now);
+             if (c->c1.tuntap)
+               tun_standby_init (c->c1.tuntap);
            }
          else
            {
diff --git a/misc.c b/misc.c
index df40ace3bf5668bb81db8016ef41610db0249d92..278e8d7c7de4e60b4c597e93c13bbdc244c19c6a 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -1372,3 +1372,33 @@ openvpn_sleep (const int n)
 #endif
   sleep (n);
 }
+
+/*
+ * Configure PATH.  On Windows, sometimes PATH is not set correctly
+ * by default.
+ */
+void
+configure_path (void)
+{
+#ifdef WIN32
+  FILE *fp;
+  fp = fopen ("c:\\windows\\system32\\route.exe", "rb");
+  if (fp)
+    {
+      const int bufsiz = 512;
+      struct gc_arena gc = gc_new ();
+      struct buffer oldpath = alloc_buf_gc (bufsiz, &gc);
+      struct buffer newpath = alloc_buf_gc (bufsiz, &gc);
+      DWORD status;
+      fclose (fp);
+      status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath));
+      if (status > 0)
+       {
+         buf_printf (&newpath, "C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;%s", BSTR(&oldpath));
+         SetEnvironmentVariable ("PATH", BSTR(&newpath));
+         /*printf ("PATH: %s\n", BSTR(&newpath));*/
+       }
+      gc_free (&gc);
+    }
+#endif
+}
diff --git a/misc.h b/misc.h
index aec12c6c041cc3d9444bd0356e44e33a291f16c6..7ec70a9111827d10cf38f29398a4d880bed9b9f3 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -257,4 +257,6 @@ const char *safe_print (const char *str, struct gc_arena *gc);
  */
 void openvpn_sleep (const int n);
 
+void configure_path (void);
+
 #endif
index 05a2d0fb0824ee8065ce3cbca4616972e690dc82..324d525be0f9594c51c7cba265ca393586160e20 100644 (file)
--- a/options.c
+++ b/options.c
@@ -612,7 +612,7 @@ init_options (struct options *o)
   o->tuntap_options.txqueuelen = 100;
 #endif
 #ifdef WIN32
-  o->tuntap_options.ip_win32_type = IPW32_SET_DHCP_MASQ;
+  o->tuntap_options.ip_win32_type = IPW32_SET_ADAPTIVE;
   o->tuntap_options.dhcp_lease_time = 31536000; /* one year */
   o->tuntap_options.dhcp_masq_offset = 0;       /* use network address as internal DHCP server address */
   o->route_method = ROUTE_METHOD_IPAPI;
@@ -1469,9 +1469,10 @@ options_postprocess (struct options *options, bool first_time)
          && !(pull || (options->ifconfig_local && options->ifconfig_remote_netmask)))
        msg (M_USAGE, "On Windows, --ip-win32 doesn't make sense unless --ifconfig is also used");
 
-      if (options->tuntap_options.dhcp_options &&
-         options->tuntap_options.ip_win32_type != IPW32_SET_DHCP_MASQ)
-       msg (M_USAGE, "--dhcp-options requires --ip-win32 dynamic");
+      if (options->tuntap_options.dhcp_options
+         && options->tuntap_options.ip_win32_type != IPW32_SET_DHCP_MASQ
+         && options->tuntap_options.ip_win32_type != IPW32_SET_ADAPTIVE)
+       msg (M_USAGE, "--dhcp-options requires --ip-win32 dynamic or adaptive");
 
       if ((dev == DEV_TYPE_TUN || dev == DEV_TYPE_TAP) && !options->route_delay_defined)
        {
@@ -4280,6 +4281,9 @@ add_option (struct options *options,
          goto err;
        }
 
+      if (index == IPW32_SET_ADAPTIVE)
+       options->route_delay_window = IPW32_SET_ADAPTIVE_DELAY_WINDOW;
+
       if (index == IPW32_SET_DHCP_MASQ)
        {
          if (p[2])
diff --git a/ping.c b/ping.c
index d91af7afcdf39f50374579fe57ab6f26da0d8d8d..bee35431c591eb6cd7d28c5bbad5ef3bc06b8d8c 100644 (file)
--- a/ping.c
+++ b/ping.c
@@ -92,5 +92,5 @@ check_ping_send_dowork (struct context *c)
    * encrypt, sign, etc.
    */
   encrypt_sign (c, true);
-  dmsg (D_PACKET_CONTENT, "SENT PING");  
+  dmsg (D_PING, "SENT PING");
 } 
index 1d564a7ac270f32bc88585aee8efeaeee90b1bc1..5c604f1c8ac6ba726bfb010661731d8a12995e41 100644 (file)
--- a/plugin.h
+++ b/plugin.h
@@ -75,7 +75,6 @@ struct plugin {
 
 struct plugin_per_client
 {
-  /* bool initialized; JYFIXME */
   void *per_client_context[MAX_PLUGINS];
 };
 
diff --git a/route.c b/route.c
index 5bde6a656557ecc80a105bc9484866bea19d6e4b..cb21489e9cb9bbb429264f758ec3e753c87ad7f6 100644 (file)
--- a/route.c
+++ b/route.c
@@ -22,6 +22,8 @@
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+/* JYFIXME WIN32 todo: add adaptive route-method */
+
 /*
  * Support routines for adding/deleting network routes.
  */
diff --git a/ssl.c b/ssl.c
index 9c494203063e9386d75eededc6ee7da788b8cfea..878a24e970a7c140207704c4e920752d2ea751d0 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -315,9 +315,7 @@ ssl_set_auth_nocache (void)
 void
 ssl_purge_auth (void)
 {
-#if 1 /* JYFIXME -- todo: bad private key should trigger a signal, then this code can be included */
   purge_user_pass (&passbuf, true);
-#endif
   purge_user_pass (&auth_user_pass, true);
 }
 
diff --git a/tun.c b/tun.c
index a518e4e264d413488ad9e9cd3574d31a87067bbd..042081750e5a96d899bc1c450ef87ea7e022f4c6 100644 (file)
--- a/tun.c
+++ b/tun.c
 
 #include "memdbg.h"
 
+#ifdef WIN32
+
+#define NI_TEST_FIRST  (1<<0)
+#define NI_IP_NETMASK  (1<<1)
+#define NI_OPTIONS     (1<<2)
+
+static void netsh_ifconfig (const struct tuntap_options *to,
+                           const char *flex_name,
+                           const in_addr_t ip,
+                           const in_addr_t netmask,
+                           unsigned int flags);
+
+static const char *netsh_get_id (const char *dev_node, struct gc_arena *gc);
+
+#endif
+
 #ifdef TARGET_SOLARIS
 static void solaris_error_close (struct tuntap *tt, const struct env_set *es, const char *actual);
 #endif
@@ -125,7 +141,7 @@ guess_tuntap_dev (const char *dev,
   const int dt = dev_type_enum (dev, dev_type);
   if (dt == DEV_TYPE_TUN || dt == DEV_TYPE_TAP)
     {
-      return get_netsh_id (dev_node, gc);
+      return netsh_get_id (dev_node, gc);
     }
 #endif
 
@@ -768,8 +784,6 @@ do_ifconfig (struct tuntap *tt,
 
 #elif defined (WIN32)
       {
-       const char *netmask;
-
        /*
         * Make sure that both ifconfig addresses are part of the
         * same .252 subnet.
@@ -778,36 +792,30 @@ do_ifconfig (struct tuntap *tt,
          {
            verify_255_255_255_252 (tt->local, tt->remote_netmask);
            tt->adapter_netmask = ~3;
-           netmask = print_in_addr_t (tt->adapter_netmask, 0, &gc);
          }
        else
          {
-           netmask = ifconfig_remote_netmask;
            tt->adapter_netmask = tt->remote_netmask;
          }
 
-       /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
-       openvpn_snprintf (command_line, sizeof (command_line),
-                         "netsh interface ip set address \"%s\" static %s %s",
-                         actual,
-                         ifconfig_local,
-                         netmask);
-       
        switch (tt->options.ip_win32_type)
          {
          case IPW32_SET_MANUAL:
            msg (M_INFO, "******** NOTE:  Please manually set the IP/netmask of '%s' to %s/%s (if it is not already set)",
                 actual,
                 ifconfig_local,
-                netmask);
+                print_in_addr_t (tt->adapter_netmask, 0, &gc));
            break;
          case IPW32_SET_NETSH:
            if (!strcmp (actual, "NULL"))
              msg (M_FATAL, "Error: When using --ip-win32 netsh, if you have more than one TAP-Win32 adapter, you must also specify --dev-node");
-           netcmd_semaphore_lock ();
-           msg (M_INFO, "%s", command_line);
-           system_check (command_line, es, S_FATAL, "ERROR: netsh command failed");
-           netcmd_semaphore_release ();
+
+           netsh_ifconfig (&tt->options,
+                           actual,
+                           tt->local,
+                           tt->adapter_netmask,
+                           NI_IP_NETMASK|NI_OPTIONS);
+
            break;
          }
        tt->did_ifconfig = true;
@@ -2262,7 +2270,7 @@ get_unspecified_device_guid (const int device_number,
       if (act)
        buf_printf (&actual, "%s", act);
       else
-       buf_printf (&actual, "NULL");
+       buf_printf (&actual, "%s", tap_reg->guid);
     }
 
   /* Save GUID for return value */
@@ -2305,7 +2313,7 @@ get_device_guid (const char *name,
       if (act)
        buf_printf (&actual, "%s", act);
       else
-       buf_printf (&actual, "NULL");
+       buf_printf (&actual, "%s", name);
       return BSTR (&ret);
     }
 
@@ -2323,39 +2331,6 @@ get_device_guid (const char *name,
   return NULL;
 }
 
-/*
- * Return a TAP name for netsh commands.
- */
-const char *
-get_netsh_id (const char *dev_node, struct gc_arena *gc)
-{
-  const struct tap_reg *tap_reg = get_tap_reg (gc);
-  const struct panel_reg *panel_reg = get_panel_reg (gc);
-  struct buffer actual = alloc_buf_gc (256, gc);
-  const char *guid;
-
-  at_least_one_tap_win32 (tap_reg);
-
-  if (dev_node)
-    {
-      guid = get_device_guid (dev_node, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
-    }
-  else
-    {
-      guid = get_unspecified_device_guid (0, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
-
-      if (get_unspecified_device_guid (1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Win32 adapter */
-       guid = NULL;
-    }
-
-  if (!guid)
-    return "NULL";         /* not found */
-  else if (strcmp (BPTR (&actual), "NULL"))
-    return BPTR (&actual); /* control panel name */
-  else
-    return guid;           /* no control panel name, return GUID instead */
-}
-
 /*
  * Get adapter info list
  */
@@ -2394,23 +2369,26 @@ get_per_adapter_info (const DWORD index, struct gc_arena *gc)
   IP_PER_ADAPTER_INFO *pi = NULL;
   DWORD status;
 
-  if ((status = GetPerAdapterInfo (index, NULL, &size)) != ERROR_BUFFER_OVERFLOW)
-    {
-      msg (M_INFO, "GetPerAdapterInfo #1 failed (status=%u) : %s",
-          (unsigned int)status,
-          strerror_win32 (status, gc));
-    }
-  else
+  if (index != ~0)
     {
-      pi = (PIP_PER_ADAPTER_INFO) gc_malloc (size, false, gc);
-      if ((status = GetPerAdapterInfo ((ULONG)index, pi, &size)) == ERROR_SUCCESS)
-       return pi;
-      else
+      if ((status = GetPerAdapterInfo (index, NULL, &size)) != ERROR_BUFFER_OVERFLOW)
        {
-         msg (M_INFO, "GetPerAdapterInfo #2 failed (status=%u) : %s",
+         msg (M_INFO, "GetPerAdapterInfo #1 failed (status=%u) : %s",
               (unsigned int)status,
               strerror_win32 (status, gc));
        }
+      else
+       {
+         pi = (PIP_PER_ADAPTER_INFO) gc_malloc (size, false, gc);
+         if ((status = GetPerAdapterInfo ((ULONG)index, pi, &size)) == ERROR_SUCCESS)
+           return pi;
+         else
+           {
+             msg (M_INFO, "GetPerAdapterInfo #2 failed (status=%u) : %s",
+                  (unsigned int)status,
+                  strerror_win32 (status, gc));
+           }
+       }
     }
   return pi;
 }
@@ -2547,6 +2525,20 @@ get_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const int n, in_addr_t *ip, i
   return ret;
 }
 
+static bool
+test_adapter_ip_netmask (const IP_ADAPTER_INFO *ai, const in_addr_t ip, const in_addr_t netmask)
+{
+  if (ai)
+    {
+      in_addr_t ip_adapter = 0;
+      in_addr_t netmask_adapter = 0;
+      const bool status = get_adapter_ip_netmask (ai, 0, &ip_adapter, &netmask_adapter);
+      return (status && ip_adapter == ip && netmask_adapter == netmask);
+    }
+  else
+    return false;
+}
+
 const IP_ADAPTER_INFO *
 get_tun_adapter (const struct tuntap *tt, const IP_ADAPTER_INFO *list)
 {
@@ -2675,16 +2667,28 @@ adapter_index_of_ip (const IP_ADAPTER_INFO *list, const in_addr_t ip, int *count
  * Given an adapter index, return true if the adapter
  * is DHCP disabled.
  */
-static bool
-dhcp_disabled (DWORD index)
+
+#define DHCP_STATUS_UNDEF     0
+#define DHCP_STATUS_ENABLED   1
+#define DHCP_STATUS_DISABLED  2
+
+static int
+dhcp_status (DWORD index)
 {
   struct gc_arena gc = gc_new ();
-  const IP_ADAPTER_INFO *ai = get_adapter_info (index, &gc);
-  bool ret = false;
-
-  if (ai && !ai->DhcpEnabled)
-    ret = true;
+  int ret = DHCP_STATUS_UNDEF;
+  if (index != ~0)
+    {
+      const IP_ADAPTER_INFO *ai = get_adapter_info (index, &gc);
 
+      if (ai)
+       {
+         if (ai->DhcpEnabled)
+           ret = DHCP_STATUS_ENABLED;
+         else
+           ret = DHCP_STATUS_DISABLED;
+       }
+    }
   gc_free (&gc);
   return ret;
 }
@@ -2733,28 +2737,75 @@ delete_temp_addresses (DWORD index)
  * Get interface index for use with IP Helper API functions.
  */
 static DWORD
-get_interface_index (const char *guid)
+get_adapter_index_method_1 (const char *guid)
 {
   struct gc_arena gc = gc_new ();
-  ULONG index;
+  ULONG index = ~0;
   DWORD status;
   wchar_t wbuf[256];
   snwprintf (wbuf, SIZE (wbuf), L"\\DEVICE\\TCPIP_%S", guid);
   wbuf [SIZE(wbuf) - 1] = 0;
   if ((status = GetAdapterIndex (wbuf, &index)) != NO_ERROR)
+    index = ~0;
+  gc_free (&gc);
+  return index;
+}
+
+static DWORD
+get_adapter_index_method_2 (const char *guid)
+{
+  struct gc_arena gc = gc_new ();
+  DWORD index = ~0;
+
+  const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
+
+  while (list)
     {
-      msg (M_INFO, "NOTE: could not get adapter index for %S, status=%u : %s",
-          wbuf,
-          (unsigned int)status,
-          strerror_win32 (status, &gc));
-      gc_free (&gc);
-      return (DWORD)~0;
-    }
-  else
-    {
-      gc_free (&gc);
-      return index;
+      if (!strcmp (guid, list->AdapterName))
+       {
+         index = list->Index;
+         break;
+       }
+      list = list->Next;
     }
+
+  gc_free (&gc);
+  return index;
+}
+
+static DWORD
+get_adapter_index (const char *guid)
+{
+  DWORD index;
+  index = get_adapter_index_method_1 (guid);
+  if (index == ~0)
+    index = get_adapter_index_method_2 (guid);
+  if (index == ~0)
+    msg (M_INFO, "NOTE: could not get adapter index for %s", guid);
+  return index;
+}
+
+static DWORD
+get_adapter_index_flexible (const char *name) /* actual name or GUID */
+{
+  struct gc_arena gc = gc_new ();
+  DWORD index;
+  index = get_adapter_index_method_1 (name);
+  if (index == ~0)
+    index = get_adapter_index_method_2 (name);
+  if (index == ~0)
+    {
+      const struct tap_reg *tap_reg = get_tap_reg (&gc);
+      const struct panel_reg *panel_reg = get_panel_reg (&gc);
+      const char *guid = name_to_guid (name, tap_reg, panel_reg);
+      index = get_adapter_index_method_1 (guid);
+      if (index == ~0)
+       index = get_adapter_index_method_2 (guid);
+    }
+  if (index == ~0)
+    msg (M_INFO, "NOTE: could not get adapter index for name/GUID '%s'", name);
+  gc_free (&gc);
+  return index;
 }
 
 /*
@@ -2869,7 +2920,7 @@ tap_allow_nonadmin_access (const char *dev_node)
   const struct panel_reg *panel_reg = get_panel_reg (&gc);
   const char *device_guid = NULL;
   HANDLE hand;
-  char guid_buffer[256];
+  char actual_buffer[256];
   char device_path[256];
 
   at_least_one_tap_win32 (tap_reg);
@@ -2877,7 +2928,7 @@ tap_allow_nonadmin_access (const char *dev_node)
   if (dev_node)
     {
       /* Get the device GUID for the device specified with --dev-node. */
-      device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), tap_reg, panel_reg, &gc);
+      device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
 
       if (!device_guid)
        msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
@@ -2912,8 +2963,8 @@ tap_allow_nonadmin_access (const char *dev_node)
       while (true)
        {
          device_guid = get_unspecified_device_guid (device_number, 
-                                                    guid_buffer, 
-                                                    sizeof (guid_buffer),
+                                                    actual_buffer, 
+                                                    sizeof (actual_buffer),
                                                     tap_reg,
                                                     panel_reg,
                                                     &gc);
@@ -3007,6 +3058,149 @@ dhcp_renew (const struct tuntap *tt)
   return ret;
 }
 
+/*
+ * netsh functions
+ */
+
+static void
+netsh_command (const char *cmd, int n)
+{
+  int i;
+  for (i = 0; i < n; ++i)
+    {
+      bool status;
+      netcmd_semaphore_lock ();
+      msg (M_INFO, "NETSH: %s", cmd);
+      status = system_check (cmd, NULL, 0, "ERROR: netsh command failed");
+      netcmd_semaphore_release ();
+      if (status)
+       return;
+      openvpn_sleep (5);
+    }
+  msg (M_FATAL, "NETSH: command failed");
+}
+
+static void
+netsh_ifconfig (const struct tuntap_options *to,
+               const char *flex_name,
+               const in_addr_t ip,
+               const in_addr_t netmask,
+               unsigned int flags)
+{
+  struct gc_arena gc = gc_new ();
+  struct buffer out = alloc_buf_gc (256, &gc);
+  const IP_ADAPTER_INFO *ai = NULL;
+  const IP_PER_ADAPTER_INFO *pai = NULL;
+
+  if (flags & NI_TEST_FIRST)
+    {
+      const IP_ADAPTER_INFO *list = get_adapter_info_list (&gc);
+      const int index = get_adapter_index_flexible (flex_name);
+      ai = get_adapter (list, index);
+      pai = get_per_adapter_info (index, &gc);
+    }
+
+  if (flags & NI_IP_NETMASK)
+    {
+      if (test_adapter_ip_netmask (ai, ip, netmask))
+       {
+         msg (M_INFO, "NETSH: \"%s\" %s/%s [already set]",
+              flex_name,
+              print_in_addr_t (ip, 0, &gc),
+              print_in_addr_t (netmask, 0, &gc));
+       }
+      else
+       {
+         /* example: netsh interface ip set address my-tap static 10.3.0.1 255.255.255.0 */
+         buf_init (&out, 0);
+         buf_printf (&out,
+                     "netsh interface ip set address \"%s\" static %s %s",
+                     flex_name,
+                     print_in_addr_t (ip, 0, &gc),
+                     print_in_addr_t (netmask, 0, &gc));
+
+         netsh_command (BSTR(&out), 4);
+       }
+    }
+
+  gc_free (&gc);
+}
+
+static void
+netsh_enable_dhcp (const struct tuntap_options *to,
+                  const char *actual_name)
+{
+  struct gc_arena gc = gc_new ();
+  struct buffer out = alloc_buf_gc (256, &gc);
+
+  /* example: netsh interface ip set address my-tap dhcp */
+  buf_printf (&out,
+             "netsh interface ip set address \"%s\" dhcp",
+             actual_name);
+
+  netsh_command (BSTR(&out), 4);
+
+  gc_free (&gc);
+}
+
+/*
+ * Return a TAP name for netsh commands.
+ */
+static const char *
+netsh_get_id (const char *dev_node, struct gc_arena *gc)
+{
+  const struct tap_reg *tap_reg = get_tap_reg (gc);
+  const struct panel_reg *panel_reg = get_panel_reg (gc);
+  struct buffer actual = alloc_buf_gc (256, gc);
+  const char *guid;
+
+  at_least_one_tap_win32 (tap_reg);
+
+  if (dev_node)
+    {
+      guid = get_device_guid (dev_node, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
+    }
+  else
+    {
+      guid = get_unspecified_device_guid (0, BPTR (&actual), BCAP (&actual), tap_reg, panel_reg, gc);
+
+      if (get_unspecified_device_guid (1, NULL, 0, tap_reg, panel_reg, gc)) /* ambiguous if more than one TAP-Win32 adapter */
+       guid = NULL;
+    }
+
+  if (!guid)
+    return "NULL";         /* not found */
+  else if (strcmp (BPTR (&actual), "NULL"))
+    return BPTR (&actual); /* control panel name */
+  else
+    return guid;           /* no control panel name, return GUID instead */
+}
+
+/*
+ * Called iteratively on TAP-Win32 wait-for-initialization polling loop
+ */
+void
+tun_standby_init (struct tuntap *tt)
+{
+  tt->standby_iter = 0;
+}
+
+void
+tun_standby (struct tuntap *tt)
+{
+  ++tt->standby_iter;
+  if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE
+      && tt->standby_iter % IPW32_SET_ADAPTIVE_TRY_NETSH == 0)
+    {
+      msg (M_INFO, "NOTE: --ip-win32 dynamic failed, now trying --ip-win32 netsh (this may take some time)");
+      netsh_ifconfig (&tt->options,
+                     tt->actual_name,
+                     tt->local,
+                     tt->adapter_netmask,
+                     NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
+    }
+}
+
 /*
  * Convert DHCP options from the command line / config file
  * into a raw DHCP-format options string.
@@ -3091,6 +3285,8 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
   char device_path[256];
   const char *device_guid = NULL;
   DWORD len;
+  bool dhcp_masq = false;
+  bool dhcp_masq_post = false;
 
   /*netcmd_semaphore_lock ();*/
 
@@ -3117,14 +3313,14 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
   {
     const struct tap_reg *tap_reg = get_tap_reg (&gc);
     const struct panel_reg *panel_reg = get_panel_reg (&gc);
-    char guid_buffer[256];
+    char actual_buffer[256];
 
     at_least_one_tap_win32 (tap_reg);
 
     if (dev_node)
       {
         /* Get the device GUID for the device specified with --dev-node. */
-        device_guid = get_device_guid (dev_node, guid_buffer, sizeof (guid_buffer), tap_reg, panel_reg, &gc);
+        device_guid = get_device_guid (dev_node, actual_buffer, sizeof (actual_buffer), tap_reg, panel_reg, &gc);
 
        if (!device_guid)
          msg (M_FATAL, "TAP-Win32 adapter '%s' not found", dev_node);
@@ -3156,8 +3352,8 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
         while (true)
           {
             device_guid = get_unspecified_device_guid (device_number, 
-                                                      guid_buffer, 
-                                                      sizeof (guid_buffer),
+                                                      actual_buffer, 
+                                                      sizeof (actual_buffer),
                                                       tap_reg,
                                                       panel_reg,
                                                       &gc);
@@ -3192,10 +3388,11 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 
     /* translate high-level device name into a device instance
        GUID using the registry */
-    tt->actual_name = string_alloc (guid_buffer, NULL);
+    tt->actual_name = string_alloc (actual_buffer, NULL);
   }
 
   msg (M_INFO, "TAP-WIN32 device [%s] opened: %s", tt->actual_name, device_path);
+  tt->adapter_index = get_adapter_index (device_guid);
 
   /* get driver version info */
   {
@@ -3230,6 +3427,42 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
       }
   }
 
+  /*
+   * Preliminaries for setting TAP-Win32 adapter TCP/IP
+   * properties via --ip-win32 dynamic or --ip-win32 adaptive.
+   */
+  if (tt->did_ifconfig_setup)
+    {
+      if (tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
+       {
+         /*
+          * If adapter is set to non-DHCP, set to DHCP mode.
+          */
+         if (dhcp_status (tt->adapter_index) == DHCP_STATUS_DISABLED)
+           netsh_enable_dhcp (&tt->options, tt->actual_name);
+         dhcp_masq = true;
+         dhcp_masq_post = true;
+       }
+      else if (tt->options.ip_win32_type == IPW32_SET_ADAPTIVE)
+       {
+         /*
+          * If adapter is set to non-DHCP, use netsh right away.
+          */
+         if (dhcp_status (tt->adapter_index) != DHCP_STATUS_ENABLED)
+           {
+             netsh_ifconfig (&tt->options,
+                             tt->actual_name,
+                             tt->local,
+                             tt->adapter_netmask,
+                             NI_TEST_FIRST|NI_IP_NETMASK|NI_OPTIONS);
+           }
+         else
+           {
+             dhcp_masq = true;
+           }
+       }
+    }
+
   /* set point-to-point mode if TUN device */
 
   if (tt->type == DEV_TYPE_TUN)
@@ -3273,7 +3506,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 
   /* should we tell the TAP-Win32 driver to masquerade as a DHCP server as a means
      of setting the adapter address? */
-  if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
+  if (dhcp_masq)
     {
       uint32_t ep[4];
 
@@ -3320,6 +3553,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 
       ASSERT (ep[3] > 0);
 
+#if 1 /* TAP_IOCTL_CONFIG_DHCP_MASQ -- disable to simulate bad DHCP negotiation */
       if (!DeviceIoControl (tt->hand, TAP_IOCTL_CONFIG_DHCP_MASQ,
                            ep, sizeof (ep),
                            ep, sizeof (ep), &len, NULL))
@@ -3332,6 +3566,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
           print_in_addr_t (ep[2], IA_NET_ORDER, &gc),
           ep[3]
           );
+#endif
 
       /* user-supplied DHCP options capability */
       if (tt->options.dhcp_options)
@@ -3368,8 +3603,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
 
   /* possibly use IP Helper API to set IP address on adapter */
   {
-    DWORD index = get_interface_index (device_guid);
-    tt->adapter_index = index;
+    const DWORD index = tt->adapter_index;
     
     /* flush arp cache */
     if (index != (DWORD)~0)
@@ -3393,10 +3627,10 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
      * make sure the TCP/IP properties for the adapter are
      * set correctly.
      */
-    if (tt->did_ifconfig_setup && tt->options.ip_win32_type == IPW32_SET_DHCP_MASQ)
+    if (dhcp_masq_post)
       {
        /* check dhcp enable status */
-       if (dhcp_disabled (index))
+       if (dhcp_status (index) == DHCP_STATUS_DISABLED)
          msg (M_WARN, "WARNING: You have selected '--ip-win32 dynamic', which will not work unless the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
 
        /* force an explicit DHCP lease renewal on TAP adapter? */
@@ -3420,7 +3654,7 @@ open_tun (const char *dev, const char *dev_type, const char *dev_node, bool ipv6
          }
 
        /* check dhcp enable status */
-       if (dhcp_disabled (index))
+       if (dhcp_status (index) == DHCP_STATUS_DISABLED)
          msg (M_WARN, "NOTE: You have selected (explicitly or by default) '--ip-win32 ipapi', which has a better chance of working correctly if the TAP-Win32 TCP/IP properties are set to 'Obtain an IP address automatically'");
 
        /* delete previously added IP addresses which were not
@@ -3556,7 +3790,8 @@ static const struct ipset_names ipset_names[] = {
   {"manual"},
   {"netsh"},
   {"ipapi"},
-  {"dynamic"}
+  {"dynamic"},
+  {"adaptive"}
 };
 
 int
diff --git a/tun.h b/tun.h
index 41264e6359c95e40bcee63112df441d3a7605d18..6f66961dbef84a5a6c47852f0c5068170dabc1f2 100644 (file)
--- a/tun.h
+++ b/tun.h
 
 #ifdef WIN32
 
+/* time constants for --ip-win32 adaptive */
+#define IPW32_SET_ADAPTIVE_DELAY_WINDOW 300
+#define IPW32_SET_ADAPTIVE_TRY_NETSH    20
+
 struct tuntap_options {
   /* --ip-win32 options */
   bool ip_win32_defined;
@@ -48,7 +52,8 @@ struct tuntap_options {
 # define IPW32_SET_NETSH        1  /* "--ip-win32 netsh" */
 # define IPW32_SET_IPAPI        2  /* "--ip-win32 ipapi" */
 # define IPW32_SET_DHCP_MASQ    3  /* "--ip-win32 dynamic" */
-# define IPW32_SET_N            4
+# define IPW32_SET_ADAPTIVE     4  /* "--ip-win32 adaptive" */
+# define IPW32_SET_N            5
   int ip_win32_type;
 
   /* --ip-win32 dynamic options */
@@ -155,6 +160,8 @@ struct tuntap
   /* Windows adapter index for TAP-Win32 adapter,
      ~0 if undefined */
   DWORD adapter_index;
+
+  int standby_iter;
 #else
   int fd;   /* file descriptor for TUN/TAP dev */
 #endif
@@ -318,12 +325,13 @@ void tun_show_debug (struct tuntap *tt);
 bool dhcp_release (const struct tuntap *tt);
 bool dhcp_renew (const struct tuntap *tt);
 
+void tun_standby_init (struct tuntap *tt);
+void tun_standby (struct tuntap *tt);
+
 int tun_read_queue (struct tuntap *tt, int maxsize);
 int tun_write_queue (struct tuntap *tt, struct buffer *buf);
 int tun_finalize (HANDLE h, struct overlapped_io *io, struct buffer *buf);
 
-const char *get_netsh_id (const char *dev_node, struct gc_arena *gc);
-
 static inline bool
 tuntap_stop (int status)
 {
@@ -379,6 +387,16 @@ tuntap_stop (int status)
   return false;
 }
 
+static inline void
+tun_standby_init (struct tuntap *tt)
+{
+}
+
+static inline void
+tun_standby (struct tuntap *tt)
+{
+}
+
 #endif
 
 /*