summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Yonan <james@openvpn.net>2010-05-24 22:51:16 +0000
committerJames Yonan <james@openvpn.net>2010-05-24 22:51:16 +0000
commit3cf6c9328250061600b78c8a7deb0edc850e739b (patch)
treebc6032117107a8e801e8203c40b1d9533b657597
parent484e9d36d09e3494a6d964c588c353f650a61b5f (diff)
downloadopenvpn-3cf6c9328250061600b78c8a7deb0edc850e739b.tar.gz
openvpn-3cf6c9328250061600b78c8a7deb0edc850e739b.tar.xz
openvpn-3cf6c9328250061600b78c8a7deb0edc850e739b.zip
Implemented http-proxy-override and http-proxy-fallback directives to make it
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
-rw-r--r--buffer.h6
-rw-r--r--forward.c17
-rw-r--r--init.c221
-rw-r--r--manage.c118
-rw-r--r--manage.h9
-rw-r--r--misc.c6
-rw-r--r--misc.h5
-rw-r--r--openvpn.c2
-rw-r--r--options.c168
-rw-r--r--options.h36
-rw-r--r--proxy.c15
-rw-r--r--proxy.h9
-rw-r--r--syshead.h23
-rw-r--r--version.m42
14 files changed, 565 insertions, 72 deletions
diff --git a/buffer.h b/buffer.h
index c2bc594..9351c4e 100644
--- 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)
{
diff --git a/forward.c b/forward.c
index 752a566..44ebd0c 100644
--- 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 456412f..a6f6bce 100644
--- 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)
diff --git a/manage.c b/manage.c
index 5fdfb95..2094723 100644
--- 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)
{
@@ -220,6 +222,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)
{
if (man_password_needed (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 */
diff --git a/manage.h b/manage.h
index a58c9a4..bb738ac 100644
--- 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 3792476..507bcc2 100644
--- 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 f788104..328107d 100644
--- 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);
/*
diff --git a/openvpn.c b/openvpn.c
index d019705..99b343b 100644
--- 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)
{
diff --git a/options.c b/options.c
index 0831e86..2c823db 100644
--- 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;
diff --git a/options.h b/options.h
index ebff532..a000ccb 100644
--- 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 7fb5b59..ac3fc65 100644
--- 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 480cda1..d89aa4a 100644
--- 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,
diff --git a/syshead.h b/syshead.h
index 3d09ce6..b159bf9 100644
--- a/syshead.h
+++ b/syshead.h
@@ -625,6 +625,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
diff --git a/version.m4 b/version.m4
index 5723d3c..4b2dcb9 100644
--- a/version.m4
+++ b/version.m4
@@ -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])