]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Implemented http-proxy-override and http-proxy-fallback directives to make it
authorJames Yonan <james@openvpn.net>
Mon, 24 May 2010 22:51:16 +0000 (22:51 +0000)
committerJames Yonan <james@openvpn.net>
Mon, 24 May 2010 22:51:16 +0000 (22:51 +0000)
easier for OpenVPN client UIs to start a pre-existing client config file with
proxy options, or to adaptively fall back to a proxy connection if a direct
connection fails.

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

14 files changed:
buffer.h
forward.c
init.c
manage.c
manage.h
misc.c
misc.h
openvpn.c
options.c
options.h
proxy.c
proxy.h
syshead.h
version.m4

index c2bc594a4fa74a81c20eb3cf24965490e46ca167..9351c4e1b15e608efb4cbb99b3cb451660ee94b3 100644 (file)
--- a/buffer.h
+++ b/buffer.h
@@ -724,6 +724,12 @@ void gc_transfer (struct gc_arena *dest, struct gc_arena *src);
 
 void x_gc_free (struct gc_arena *a);
 
+static inline bool
+gc_defined (struct gc_arena *a)
+{
+  return a->list != NULL;
+}
+
 static inline void
 gc_init (struct gc_arena *a)
 {
index 752a566b8f52bd62a66f5c19c9a0a3bafbfbd1e2..44ebd0c923ca1ffa71b7e4af383cde503e7592cb 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -687,14 +687,25 @@ read_incoming_link (struct context *c)
        if (c->options.inetd)
          {
            c->sig->signal_received = SIGTERM;
+           c->sig->signal_text = "connection-reset-inetd";
            msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status);
          }
        else
          {
-           c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */
-           msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status);
+#ifdef ENABLE_OCC
+           if (event_timeout_defined(&c->c2.explicit_exit_notification_interval))
+             {
+               msg (D_STREAM_ERRORS, "Connection reset during exit notification period, ignoring [%d]", status);
+               openvpn_sleep(1);
+             }
+           else
+#endif
+             {
+               c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */
+               c->sig->signal_text = "connection-reset";
+               msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status);
+             }
          }
-       c->sig->signal_text = "connection-reset";
       }
       perf_pop ();
       return;
diff --git a/init.c b/init.c
index 456412f4337c3396e48568334a1c0f9c5ec7c4e0..a6f6bcebcb35d19865f5c797459d9742b64bf1a6 100644 (file)
--- a/init.c
+++ b/init.c
@@ -111,6 +111,103 @@ update_options_ce_post (struct options *options)
 #endif
 }
 
+#if HTTP_PROXY_FALLBACK
+
+static bool
+ce_http_proxy_fallback_defined(const struct context *c)
+{
+  const struct connection_list *l = c->options.connection_list;
+  if (l && l->current == 0)
+    {
+      int i;
+      for (i = 0; i < l->len; ++i)
+       {
+         if (l->array[i]->flags & CE_HTTP_PROXY_FALLBACK)
+           return true;
+       }
+    }
+  return false;
+}
+
+static void
+ce_http_proxy_fallback_start(struct context *c, const char *remote_ip_hint)
+{
+  const struct connection_list *l = c->options.connection_list;
+  if (l)
+    {
+      int i;
+      for (i = 0; i < l->len; ++i)
+       {
+         struct connection_entry *ce = l->array[i];
+         if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+           {
+             ce->http_proxy_options = NULL;
+             ce->ce_http_proxy_fallback_timestamp = 0;
+             if (!remote_ip_hint)
+               remote_ip_hint = ce->remote;
+           }
+       }
+    }
+
+  if (management)
+    management_http_proxy_fallback_notify(management, "NEED_LATER", remote_ip_hint);
+}
+
+static bool
+ce_http_proxy_fallback (struct context *c, volatile const struct connection_entry *ce)
+{
+  const int proxy_info_expire = 120; /* seconds before proxy info expires */
+
+  update_time();
+  if (management)
+    {
+      if (!ce->ce_http_proxy_fallback_timestamp)
+       {
+         management_http_proxy_fallback_notify(management, "NEED_NOW", NULL);
+         while (!ce->ce_http_proxy_fallback_timestamp)
+           {
+             management_event_loop_n_seconds (management, 1);
+             if (IS_SIG (c))
+               return false;
+           }
+       }
+      return (now < ce->ce_http_proxy_fallback_timestamp + proxy_info_expire && ce->http_proxy_options);
+    }
+  return false;
+}
+
+static bool
+management_callback_http_proxy_fallback_cmd (void *arg, const char *server, const char *port, const char *flags)
+{
+  struct context *c = (struct context *) arg;
+  const struct connection_list *l = c->options.connection_list;
+  int ret = false;
+  struct http_proxy_options *ho = parse_http_proxy_fallback (c, server, port, flags, M_WARN);
+
+  update_time();
+  if (l)
+    {
+      int i;
+      for (i = 0; i < l->len; ++i)
+       {
+         struct connection_entry *ce = l->array[i];
+         if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+           {
+             if (ho)
+               {
+                 ce->http_proxy_options = ho;
+                 ret = true;
+               }
+             ce->ce_http_proxy_fallback_timestamp = now;
+           }
+       }
+    }
+  
+  return ret;
+}
+
+#endif
+
 /*
  * Initialize and possibly randomize connection list.
  */
@@ -141,6 +238,30 @@ init_connection_list (struct context *c)
 #endif
 }
 
+#if 0 /* fixme -- disable for production */
+static void
+show_connection_list (const struct connection_list *l)
+{
+  int i;
+  dmsg (M_INFO, "CONNECTION_LIST len=%d current=%d",
+       l->len, l->current);
+  for (i = 0; i < l->len; ++i)
+    {
+      dmsg (M_INFO, "[%d] %s:%d proto=%s http_proxy=%d",
+           i,
+           l->array[i]->remote,
+           l->array[i]->remote_port,
+           proto2ascii(l->array[i]->proto, true),
+           BOOL_CAST(l->array[i]->http_proxy_options));
+    }
+}
+#else
+static inline void
+show_connection_list (const struct connection_list *l)
+{
+}
+#endif
+
 /*
  * Increment to next connection entry
  */
@@ -151,27 +272,65 @@ next_connection_entry (struct context *c)
   struct connection_list *l = c->options.connection_list;
   if (l)
     {
-      if (l->no_advance && l->current >= 0)
-       {
-         l->no_advance = false;
-       }
-      else
-       {
-         int i;
-         if (++l->current >= l->len)
-           l->current = 0;
+      bool ce_defined;
+      struct connection_entry *ce;
+      int n_cycles = 0;
 
-         dmsg (D_CONNECTION_LIST, "CONNECTION_LIST len=%d current=%d",
-               l->len, l->current);
-         for (i = 0; i < l->len; ++i)
-           {
-             dmsg (D_CONNECTION_LIST, "[%d] %s:%d",
-                   i,
-                   l->array[i]->remote,
-                   l->array[i]->remote_port);
-           }
-       }
-      c->options.ce = *l->array[l->current];
+      do {
+       const char *remote_ip_hint = NULL;
+       bool advanced = false;
+
+       ce_defined = true;
+       if (l->no_advance && l->current >= 0)
+         {
+           l->no_advance = false;
+         }
+       else
+         {
+           if (++l->current >= l->len)
+             {
+               l->current = 0;
+               ++l->n_cycles;
+               if (++n_cycles >= 2)
+                 msg (M_FATAL, "No usable connection profiles are present");
+             }
+
+           advanced = true;
+           show_connection_list(l);
+         }
+
+       ce = l->array[l->current];
+
+       if (c->options.remote_ip_hint && !l->n_cycles)
+         remote_ip_hint = c->options.remote_ip_hint;
+
+#if HTTP_PROXY_FALLBACK
+       if (advanced && ce_http_proxy_fallback_defined(c))
+         ce_http_proxy_fallback_start(c, remote_ip_hint);
+
+       if (ce->flags & CE_HTTP_PROXY_FALLBACK)
+         {
+           ce_defined = ce_http_proxy_fallback(c, ce);
+           if (IS_SIG (c))
+             break;
+         }
+#endif
+
+       if (ce->flags & CE_DISABLED)
+         ce_defined = false;
+
+       c->options.ce = *ce;
+
+       if (remote_ip_hint)
+         c->options.ce.remote = remote_ip_hint;
+
+#if 0 /* fixme -- disable for production, this code simulates a network where proxy fallback is the only method to reach the OpenVPN server */
+       if (!(c->options.ce.flags & CE_HTTP_PROXY_FALLBACK))
+         {
+           c->options.ce.remote = "10.10.0.1"; /* use an unreachable address here */
+         }
+#endif
+      } while (!ce_defined);
     }
 #endif
   update_options_ce_post (&c->options);
@@ -2774,6 +2933,9 @@ init_management_callback_p2p (struct context *c)
       cb.arg = c;
       cb.status = management_callback_status_p2p;
       cb.show_net = management_show_net_callback;
+#if HTTP_PROXY_FALLBACK
+      cb.http_proxy_fallback_cmd = management_callback_http_proxy_fallback_cmd;
+#endif
       management_set_callback (management, &cb);
     }
 #endif
@@ -2898,6 +3060,17 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
   c->sig->signal_text = NULL;
   c->sig->hard = false;
 
+  if (c->mode == CM_P2P)
+    init_management_callback_p2p (c);
+
+  /* possible sleep or management hold if restart */
+  if (c->mode == CM_P2P || c->mode == CM_TOP)
+    {
+      do_startup_pause (c);
+      if (IS_SIG (c))
+       goto sig;
+    }
+
   /* map in current connection entry */
   next_connection_entry (c);
 
@@ -2916,14 +3089,6 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int
   if (c->first_time && options->mlock)
     do_mlockall (true);
 
-  /* possible sleep or management hold if restart */
-  if (c->mode == CM_P2P || c->mode == CM_TOP)
-    {
-      do_startup_pause (c);
-      if (IS_SIG (c))
-       goto sig;
-    }
-
 #if P2MP
   /* get passwords if undefined */
   if (auth_retry_get () == AR_INTERACT)
index 5fdfb955d420ce7865617e7f80d68ee80d452583..209472330338e8110eea7183302d0df566e724a3 100644 (file)
--- a/manage.c
+++ b/manage.c
@@ -110,6 +110,10 @@ man_help ()
   msg (M_CLIENT, "username type u        : Enter username u for a queried OpenVPN username.");
   msg (M_CLIENT, "verb [n]               : Set log verbosity level to n, or show if n is absent.");
   msg (M_CLIENT, "version                : Show current version number.");
+#if HTTP_PROXY_FALLBACK
+  msg (M_CLIENT, "http-proxy-fallback <server> <port> [flags] : Enter dynamic HTTP proxy fallback info.");
+  msg (M_CLIENT, "http-proxy-fallback-disable : Disable HTTP proxy fallback.");
+#endif
   msg (M_CLIENT, "END");
 }
 
@@ -204,12 +208,10 @@ man_update_io_state (struct management *man)
 }
 
 static void
-man_output_list_push (struct management *man, const char *str)
+man_output_list_push_finalize (struct management *man)
 {
   if (management_connected (man))
     {
-      if (str)
-       buffer_list_push (man->connection.out, (const unsigned char *) str);
       man_update_io_state (man);
       if (!man->persist.standalone_disabled)
        {
@@ -219,6 +221,22 @@ man_output_list_push (struct management *man, const char *str)
     }
 }
 
+static void
+man_output_list_push_str (struct management *man, const char *str)
+{
+  if (management_connected (man) && str)
+    {
+      buffer_list_push (man->connection.out, (const unsigned char *) str);
+    }
+}
+
+static void
+man_output_list_push (struct management *man, const char *str)
+{
+  man_output_list_push_str (man, str);
+  man_output_list_push_finalize (man);
+}
+
 static void
 man_prompt (struct management *man)
 {
@@ -256,12 +274,13 @@ man_close_socket (struct management *man, const socket_descriptor_t sd)
 static void
 virtual_output_callback_func (void *arg, const unsigned int flags, const char *str)
 {
+  struct management *man = (struct management *) arg;
   static int recursive_level = 0; /* GLOBAL */
+  bool did_push = false;
 
   if (!recursive_level) /* don't allow recursion */
     {
       struct gc_arena gc = gc_new ();
-      struct management *man = (struct management *) arg;
       struct log_entry e;
       const char *out = NULL;
 
@@ -289,13 +308,17 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s
                                   |   LOG_PRINT_LOG_PREFIX
                                   |   LOG_PRINT_CRLF, &gc);
          if (out)
-           man_output_list_push (man, out);
+           {
+             man_output_list_push_str (man, out);
+             did_push = true;
+           }
          if (flags & M_FATAL)
            {
              out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc);
              if (out)
                {
-                 man_output_list_push (man, out);
+                 man_output_list_push_str (man, out);
+                 did_push = true;
                  man_reset_client_socket (man, true);
                }
            }
@@ -304,6 +327,9 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s
       --recursive_level;
       gc_free (&gc);
     }
+
+  if (did_push)
+    man_output_list_push_finalize (man);
 }
 
 /*
@@ -998,6 +1024,31 @@ man_need (struct management *man, const char **p, const int n, unsigned int flag
   return true;
 }
 
+#if HTTP_PROXY_FALLBACK
+
+static void
+man_http_proxy_fallback (struct management *man, const char *server, const char *port, const char *flags)
+{
+  if (man->persist.callback.http_proxy_fallback_cmd)
+    {
+      const bool status = (*man->persist.callback.http_proxy_fallback_cmd)(man->persist.callback.arg, server, port, flags);
+      if (status)
+       {
+         msg (M_CLIENT, "SUCCESS: proxy-fallback command succeeded");
+       }
+      else
+       {
+         msg (M_CLIENT, "ERROR: proxy-fallback command failed");
+       }
+    }
+  else
+    {
+      msg (M_CLIENT, "ERROR: The proxy-fallback command is not supported by the current daemon mode");
+    }
+}
+
+#endif
+
 static void
 man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms)
 {
@@ -1210,6 +1261,17 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
        man_pkcs11_id_get (man, atoi(p[1]));
     }
 #endif
+#if HTTP_PROXY_FALLBACK
+  else if (streq (p[0], "http-proxy-fallback"))
+    {
+      if (man_need (man, p, 2, MN_AT_LEAST))
+       man_http_proxy_fallback (man, p[1], p[2], p[3]);
+    }
+  else if (streq (p[0], "http-proxy-fallback-disable"))
+    {
+      man_http_proxy_fallback (man, NULL, NULL, NULL);
+    }
+#endif
 #if 1
   else if (streq (p[0], "test"))
     {
@@ -2103,7 +2165,7 @@ management_clear_callback (struct management *man)
   man->persist.standalone_disabled = false;
   man->persist.hold_release = false;
   CLEAR (man->persist.callback);
-  man_output_list_push (man, NULL); /* flush output queue */
+  man_output_list_push_finalize (man); /* flush output queue */
 }
 
 void
@@ -2402,7 +2464,7 @@ management_io (struct management *man)
                  net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT);
                }
            }
-         else if (man->connection.state == MS_CC_WAIT_READ)
+         else if (man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE)
            {
              if (net_events & FD_READ)
                {
@@ -2410,18 +2472,13 @@ management_io (struct management *man)
                    ;
                  net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ);
                }
-           }
 
-         if (man->connection.state == MS_CC_WAIT_WRITE)
-           {
              if (net_events & FD_WRITE)
                {
                  int status;
-                 /* dmsg (M_INFO, "FD_WRITE set"); */
                  status = man_write (man);
                  if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
                    {
-                     /* dmsg (M_INFO, "FD_WRITE cleared"); */
                      net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE);
                    }
                }
@@ -2512,7 +2569,7 @@ man_block (struct management *man, volatile int *signal_received, const time_t e
   
   if (man_standalone_ok (man))
     {
-      do
+      while (true)
        {
          event_reset (man->connection.es);
          management_socket_set (man, man->connection.es, NULL, NULL);
@@ -2530,15 +2587,18 @@ man_block (struct management *man, volatile int *signal_received, const time_t e
              status = -1;
              break;
            }
-         /* set SIGINT signal if expiration time exceeded */
-         if (expire && now >= expire)
+
+         if (status > 0)
+           break;
+         else if (expire && now >= expire)
            {
+             /* set SIGINT signal if expiration time exceeded */
              status = 0;
              if (signal_received)
                *signal_received = SIGINT;
              break;
            }
-       } while (status != 1);
+       }
     }
   return status;
 }
@@ -2615,28 +2675,29 @@ management_event_loop_n_seconds (struct management *man, int sec)
     {
       volatile int signal_received = 0;
       const bool standalone_disabled_save = man->persist.standalone_disabled;
-      time_t expire;
+      time_t expire = 0;
 
       man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
 
       /* set expire time */
       update_time ();
-      expire = now + sec;
+      if (sec)
+       expire = now + sec;
 
       /* if no client connection, wait for one */
       man_wait_for_client_connection (man, &signal_received, expire, 0);
       if (signal_received)
        return;
 
-      /* run command processing event loop until we get our username/password */
-      while (true)
+      /* run command processing event loop */
+      do
        {
          man_standalone_event_loop (man, &signal_received, expire);
          if (!signal_received)
            man_check_for_signals (&signal_received);
          if (signal_received)
            return;
-       }
+       } while (expire);
 
       /* revert state */
       man->persist.standalone_disabled = standalone_disabled_save;
@@ -3028,6 +3089,19 @@ log_history_ref (const struct log_history *h, const int index)
     return NULL;
 }
 
+#if HTTP_PROXY_FALLBACK
+
+void
+management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint)
+{
+  if (remote_ip_hint)
+    msg (M_CLIENT, ">PROXY:%s,%s", type, remote_ip_hint);
+  else
+    msg (M_CLIENT, ">PROXY:%s", type);
+}
+
+#endif /* HTTP_PROXY_FALLBACK */
+
 #else
 static void dummy(void) {}
 #endif /* ENABLE_MANAGEMENT */
index a58c9a45da44571d3b2d66deee48352996ea7d31..bb738ac280e0ee1c3694ecea8c29c21f3bc5e309 100644 (file)
--- a/manage.h
+++ b/manage.h
@@ -170,6 +170,9 @@ struct management_callback
                     const unsigned long cid,
                     struct buffer_list *pf_config);   /* ownership transferred */
 #endif
+#if HTTP_PROXY_FALLBACK
+  bool (*http_proxy_fallback_cmd) (void *arg, const char *server, const char *port, const char *flags);
+#endif
 };
 
 /*
@@ -502,5 +505,11 @@ management_bytes_server (struct management *man,
 
 #endif /* MANAGEMENT_DEF_AUTH */
 
+#if HTTP_PROXY_FALLBACK
+
+void management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint);
+
+#endif /* HTTP_PROXY_FALLBACK */
+
 #endif
 #endif
diff --git a/misc.c b/misc.c
index 379247619f95a221b63d705f5ddbcaf544473620..507bcc2ca17d9a93ccef1288f6d193f40afe496d 100644 (file)
--- a/misc.c
+++ b/misc.c
@@ -1374,6 +1374,9 @@ get_user_pass (struct user_pass *up,
     {
       const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin"));
 
+      if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
+       msg (M_WARN, "Note: previous '%s' credentials failed", prefix);
+
 #ifdef ENABLE_MANAGEMENT
       /*
        * Get username/password from standard input?
@@ -1382,6 +1385,9 @@ get_user_pass (struct user_pass *up,
          && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
          && management_query_user_pass_enabled (management))
        {
+         if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
+           management_auth_failure (management, prefix, "previous auth credentials failed");
+
          if (!management_query_user_pass (management, up, prefix, flags))
            {
              if ((flags & GET_USER_PASS_NOFATAL) != 0)
diff --git a/misc.h b/misc.h
index f788104b505cb5520e4ca28e742970af22152a93..328107dedf90c0f446bb38a88b5d306878fd91ff 100644 (file)
--- a/misc.h
+++ b/misc.h
@@ -263,12 +263,17 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const
 #define GET_USER_PASS_NEED_OK       (1<<3)
 #define GET_USER_PASS_NOFATAL       (1<<4)
 #define GET_USER_PASS_NEED_STR      (1<<5)
+#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)
 
 bool get_user_pass (struct user_pass *up,
                    const char *auth_file,
                    const char *prefix,
                    const unsigned int flags);
 
+void fail_user_pass (const char *prefix,
+                    const unsigned int flags,
+                    const char *reason);
+
 void purge_user_pass (struct user_pass *up, const bool force);
 
 /*
index d019705137cc96e216edce32de84f8843b2c101f..99b343bb2aedf805714724cdeebd06ffec0c5b5a 100644 (file)
--- a/openvpn.c
+++ b/openvpn.c
@@ -55,8 +55,6 @@ tunnel_point_to_point (struct context *c)
   if (IS_SIG (c))
     return;
 
-  init_management_callback_p2p (c);
-
   /* main event loop */
   while (true)
     {
index 0831e867f239cc360ca701fc768e365feaacb9c4..2c823db08507ec78bd267e8e4b4bbc5464c83f97 100644 (file)
--- a/options.c
+++ b/options.c
@@ -761,7 +761,9 @@ void
 uninit_options (struct options *o)
 {
   if (o->gc_owned)
-    gc_free (&o->gc);
+    {
+      gc_free (&o->gc);
+    }
 }
 
 #ifdef ENABLE_DEBUG
@@ -1412,6 +1414,137 @@ init_http_options_if_undefined (struct options *o)
 
 #endif
 
+#if HTTP_PROXY_FALLBACK
+
+static struct http_proxy_options *
+parse_http_proxy_override (const char *server,
+                          const char *port,
+                          const char *flags,
+                          const int msglevel,
+                          struct gc_arena *gc)
+{
+  if (server && port)
+    {
+      struct http_proxy_options *ho;
+      const int int_port = atoi(port);
+
+      if (!legal_ipv4_port (int_port))
+       {
+         msg (msglevel, "Bad http-proxy port number: %s", port);
+         return NULL;
+       }
+
+      ALLOC_OBJ_CLEAR_GC (ho, struct http_proxy_options, gc);
+      ho->server = string_alloc(server, gc);
+      ho->port = int_port;
+      ho->retry = true;
+      ho->timeout = 5;
+      if (flags && !strcmp(flags, "nct"))
+       ho->auth_retry = PAR_NCT;
+      else
+       ho->auth_retry = PAR_ALL;
+      ho->http_version = "1.0";
+      ho->user_agent = "OpenVPN-Autoproxy/1.0";
+      return ho;
+    }
+  else
+    return NULL;
+}
+
+struct http_proxy_options *
+parse_http_proxy_fallback (struct context *c,
+                          const char *server,
+                          const char *port,
+                          const char *flags,
+                          const int msglevel)
+{
+  struct gc_arena gc = gc_new ();  
+  struct http_proxy_options *hp = parse_http_proxy_override(server, port, flags, msglevel, &gc);
+  struct hpo_store *hpos = c->options.hpo_store;
+  if (!hpos)
+    {
+      ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc);
+      c->options.hpo_store = hpos;
+    }
+  hpos->hpo = *hp;
+  hpos->hpo.server = hpos->server;
+  strncpynt(hpos->server, hp->server, sizeof(hpos->server));
+  gc_free (&gc);
+  return &hpos->hpo;
+}
+
+static void
+http_proxy_warn(const char *name)
+{
+  msg (M_WARN, "Note: option %s ignored because no TCP-based connection profiles are defined", name);
+}
+
+void
+options_postprocess_http_proxy_fallback (struct options *o)
+{
+  struct connection_list *l = o->connection_list;
+  if (l)
+    {
+      int i;
+      for (i = 0; i < l->len; ++i)
+       {
+         struct connection_entry *ce = l->array[i];
+         if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4)
+           {
+             if (l->len < CONNECTION_LIST_SIZE)
+               {
+                 struct connection_entry *newce;
+                 ALLOC_OBJ_GC (newce, struct connection_entry, &o->gc);
+                 *newce = *ce;
+                 newce->flags |= CE_HTTP_PROXY_FALLBACK;
+                 newce->http_proxy_options = NULL;
+                 newce->ce_http_proxy_fallback_timestamp = 0;
+                 l->array[l->len++] = newce;
+               }
+             return;
+           }
+       }
+    }
+  http_proxy_warn("http-proxy-fallback");
+}
+
+void
+options_postprocess_http_proxy_override (struct options *o)
+{
+  const struct connection_list *l = o->connection_list;
+   if (l)
+    {
+      int i;
+      bool succeed = false;
+      for (i = 0; i < l->len; ++i)
+       {
+         struct connection_entry *ce = l->array[i];
+         if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4)
+           {
+             ce->http_proxy_options = o->http_proxy_override;
+             succeed = true;
+           }
+       }
+      if (succeed)
+       {
+         for (i = 0; i < l->len; ++i)
+           {
+             struct connection_entry *ce = l->array[i];
+             if (ce->proto == PROTO_UDPv4)
+               {
+                 ce->flags |= CE_DISABLED;
+               }
+           }
+       }
+      else
+       {
+         http_proxy_warn("http-proxy-override");
+       }
+    }
+}
+
+#endif
+
 #if ENABLE_CONNECTION
 
 static struct connection_list *
@@ -2095,7 +2228,7 @@ options_postprocess_mutate (struct options *o)
        * For compatibility with 2.0.x, map multiple --remote options
        * into connection list (connection lists added in 2.1).
        */
-      if (o->remote_list->len > 1)
+      if (o->remote_list->len > 1 || o->force_connection_list)
        {
          const struct remote_list *rl = o->remote_list;
          int i;
@@ -2112,7 +2245,7 @@ options_postprocess_mutate (struct options *o)
              *ace = ce;
            }
        }
-      else if (o->remote_list->len == 1) /* one --remote option specfied */
+      else if (o->remote_list->len == 1) /* one --remote option specified */
        {
          connection_entry_load_re (&o->ce, o->remote_list->array[0]);
        }
@@ -2126,6 +2259,13 @@ options_postprocess_mutate (struct options *o)
       int i;
       for (i = 0; i < o->connection_list->len; ++i)
        options_postprocess_mutate_ce (o, o->connection_list->array[i]);
+
+#if HTTP_PROXY_FALLBACK
+      if (o->http_proxy_override)
+       options_postprocess_http_proxy_override(o);
+      else if (o->http_proxy_fallback)
+       options_postprocess_http_proxy_fallback(o);
+#endif
     }
   else
 #endif
@@ -3633,11 +3773,29 @@ add_option (struct options *options,
        }
     }
 #endif
+#ifdef ENABLE_CONNECTION
   else if (streq (p[0], "remote-ip-hint") && p[1])
     {
-      VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION);
-      // fixme
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->remote_ip_hint = p[1];
     }
+#endif
+#if HTTP_PROXY_FALLBACK
+  else if (streq (p[0], "http-proxy-fallback"))
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->http_proxy_fallback = true;
+      options->force_connection_list = true;
+    }
+  else if (streq (p[0], "http-proxy-override") && p[1] && p[2])
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->http_proxy_override = parse_http_proxy_override(p[1], p[2], p[3], msglevel, &options->gc);
+      if (!options->http_proxy_override)
+       goto err;
+      options->force_connection_list = true;
+    }
+#endif
   else if (streq (p[0], "remote") && p[1])
     {
       struct remote_entry re;
index ebff532ac29515678b8ecf2c32b2c78b4bc2b9be..a000ccb4505bc1a5972dcdb45675e6417483dcb8 100644 (file)
--- a/options.h
+++ b/options.h
@@ -97,6 +97,14 @@ struct connection_entry
   int socks_proxy_port;
   bool socks_proxy_retry;
 #endif
+
+# define CE_DISABLED (1<<0)
+#if HTTP_PROXY_FALLBACK
+# define CE_HTTP_PROXY_FALLBACK (1<<1)
+  time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */
+#endif
+
+  unsigned int flags;
 };
 
 struct remote_entry
@@ -114,6 +122,7 @@ struct connection_list
 {
   int len;
   int current;
+  int n_cycles;
   bool no_advance;
   struct connection_entry *array[CONNECTION_LIST_SIZE];
 };
@@ -126,6 +135,14 @@ struct remote_list
 
 #endif
 
+#if HTTP_PROXY_FALLBACK
+struct hpo_store
+{
+  struct http_proxy_options hpo;
+  char server[80];
+};
+#endif
+
 /* Command line options */
 struct options
 {
@@ -162,14 +179,22 @@ struct options
   struct connection_entry ce;
 
 #ifdef ENABLE_CONNECTION
+  char *remote_ip_hint;
   struct connection_list *connection_list;
   struct remote_list *remote_list;
+  bool force_connection_list;
 #endif
 
 #ifdef GENERAL_PROXY_SUPPORT
   struct auto_proxy_info *auto_proxy_info;
 #endif
 
+#if HTTP_PROXY_FALLBACK
+  bool http_proxy_fallback;
+  struct http_proxy_options *http_proxy_override;
+  struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */
+#endif
+
   bool remote_random;
   const char *ipchange;
   const char *dev;
@@ -710,4 +735,15 @@ connection_list_set_no_advance (struct options *o)
 #endif
 }
 
+#if HTTP_PROXY_FALLBACK
+
+struct http_proxy_options *
+parse_http_proxy_fallback (struct context *c,
+                          const char *server,
+                          const char *port,
+                          const char *flags,
+                          const int msglevel);
+
+#endif /* HTTP_PROXY_FALLBACK */
+
 #endif
diff --git a/proxy.c b/proxy.c
index 7fb5b59e92bccb10dd21d6c490d7b1ebfe5fafac..ac3fc65ff03ab8c2ee5b216d11b5fec0fa6445db 100644 (file)
--- a/proxy.c
+++ b/proxy.c
@@ -224,10 +224,14 @@ get_user_pass_http (struct http_proxy_info *p, const bool force)
 {
   if (!static_proxy_user_pass.defined || force)
     {
+      unsigned int flags = GET_USER_PASS_MANAGEMENT;
+      if (p->queried_creds)
+       flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED;
       get_user_pass (&static_proxy_user_pass,
                     p->options.auth_file,
                     UP_TYPE_PROXY,
-                    GET_USER_PASS_MANAGEMENT);
+                    flags);
+      p->queried_creds = true;
       p->up = static_proxy_user_pass;
     }
 }
@@ -755,12 +759,12 @@ establish_http_proxy_passthru (struct http_proxy_info *p,
                            realm,
                            password,
                            nonce,
-                           cnonce,
+                           (char *)cnonce,
                            session_key);
              DigestCalcResponse(session_key,
                                 nonce,
                                 nonce_count,
-                                cnonce,
+                                (char *)cnonce,
                                 qop,
                                 http_method,
                                 uri,
@@ -877,6 +881,8 @@ establish_http_proxy_passthru (struct http_proxy_info *p,
       goto error;
     }
 
+  /* SUCCESS */
+
   /* receive line from proxy and discard */
   if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received))
     goto error;
@@ -888,6 +894,9 @@ establish_http_proxy_passthru (struct http_proxy_info *p,
   while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received))
     ;
 
+  /* reset queried_creds so that we don't think that the next creds request is due to an auth error */
+  p->queried_creds = false;
+
 #if 0
   if (lookahead && BLEN (lookahead))
     msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0));
diff --git a/proxy.h b/proxy.h
index 480cda14203429e4443bed7570bb5f4a67a90a43..d89aa4affee722df35233e1d94225d3b6785b31f 100644 (file)
--- a/proxy.h
+++ b/proxy.h
@@ -71,7 +71,7 @@ struct http_proxy_options {
 # define PAR_NO  0  /* don't support any auth retries */
 # define PAR_ALL 1  /* allow all proxy auth protocols */
 # define PAR_NCT 2  /* disable cleartext proxy auth protocols */
-  bool auth_retry;
+  int auth_retry;
 
   const char *auth_method_string;
   const char *auth_file;
@@ -79,12 +79,19 @@ struct http_proxy_options {
   const char *user_agent;
 };
 
+struct http_proxy_options_simple {
+  const char *server;
+  int port;
+  int auth_retry;
+};
+
 struct http_proxy_info {
   bool defined;
   int auth_method;
   struct http_proxy_options options;
   struct user_pass up;
   char *proxy_authenticate;
+  bool queried_creds;
 };
 
 struct http_proxy_info *http_proxy_new (const struct http_proxy_options *o,
index 3d09ce61102123027bc3d1063614fd103e6cbc52..b159bf9315e70b044aebf9ee1940961574cf686a 100644 (file)
--- a/syshead.h
+++ b/syshead.h
@@ -624,6 +624,22 @@ socket_defined (const socket_descriptor_t sd)
  */
 #define ENABLE_INLINE_FILES 1
 
+/*
+ * Support "connection" directive
+ */
+#if ENABLE_INLINE_FILES
+#define ENABLE_CONNECTION 1
+#endif
+
+/*
+ * Should we include http proxy fallback functionality
+ */
+#if defined(ENABLE_CONNECTION) && defined(ENABLE_MANAGEMENT) && defined(ENABLE_HTTP_PROXY)
+#define HTTP_PROXY_FALLBACK 1
+#else
+#define HTTP_PROXY_FALLBACK 0
+#endif
+
 /*
  * Reduce sensitivity to system clock instability
  * and backtracks.
@@ -646,11 +662,4 @@ socket_defined (const socket_descriptor_t sd)
 #define AUTO_USERID 0
 #endif
 
-/*
- * Support "connection" directive
- */
-#if ENABLE_INLINE_FILES
-#define ENABLE_CONNECTION 1
-#endif
-
 #endif
index 5723d3c50eb91623e7a959bed92e9a2389b30ea3..4b2dcb9df2ea29b5115073b2eed675fb5d92d331 100644 (file)
@@ -1,5 +1,5 @@
 dnl define the OpenVPN version
-define(PRODUCT_VERSION,[2.1.1g])
+define(PRODUCT_VERSION,[2.1.1h])
 dnl define the TAP version
 define(PRODUCT_TAP_ID,[tap0901])
 define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])