aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sommerseth <dazo@users.sourceforge.net>2010-06-04 23:18:03 +0200
committerDavid Sommerseth <dazo@users.sourceforge.net>2010-06-04 23:18:03 +0200
commite0ca5fdf8f54c3c264fcb1ddaf0adbde212d858a (patch)
tree202c201b860f996ccf754d3bfc256658c3694dc4
parentOCSP_check.sh: new check logic (diff)
parentMerge branch 'svn-BETA21' (diff)
downloadopenvpn-e0ca5fdf8f54c3c264fcb1ddaf0adbde212d858a.tar.xz
Merge branch 'master' into bugfix2.1
-rw-r--r--buffer.h6
-rw-r--r--common.h9
-rw-r--r--forward.c17
-rw-r--r--init.c224
-rw-r--r--manage.c171
-rw-r--r--manage.h10
-rw-r--r--misc.c6
-rw-r--r--misc.h5
-rw-r--r--multi.c15
-rw-r--r--openvpn.c2
-rw-r--r--options.c184
-rw-r--r--options.h40
-rw-r--r--proxy.c15
-rw-r--r--proxy.h9
-rw-r--r--push.c6
-rw-r--r--route.c2
-rw-r--r--route.h2
-rw-r--r--ssl.c124
-rw-r--r--ssl.h15
-rw-r--r--syshead.h22
-rw-r--r--version.m42
21 files changed, 809 insertions, 77 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/common.h b/common.h
index b1f5818..5548f7c 100644
--- a/common.h
+++ b/common.h
@@ -76,8 +76,15 @@ typedef unsigned long ptr_type;
/*
* This parameter controls the TLS channel buffer size and the
* maximum size of a single TLS message (cleartext).
+ * This parameter must be >= PUSH_BUNDLE_SIZE
*/
-#define TLS_CHANNEL_BUF_SIZE 1024
+#define TLS_CHANNEL_BUF_SIZE 2048
+
+/*
+ * This parameter controls the maximum size of a bundle
+ * of pushed options.
+ */
+#define PUSH_BUNDLE_SIZE 1024
/*
* A sort of pseudo-filename for data provided inline within
diff --git a/forward.c b/forward.c
index 4edbe59..a0d67d0 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 8ec6ca6..4b2a8d0 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);
@@ -1848,6 +2007,9 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.renegotiate_packets = options->renegotiate_packets;
to.renegotiate_seconds = options->renegotiate_seconds;
to.single_session = options->single_session;
+#ifdef ENABLE_PUSH_PEER_INFO
+ to.push_peer_info = options->push_peer_info;
+#endif
/* should we not xmit any packets until we get an initial
response from client? */
@@ -2774,6 +2936,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 +3063,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 +3092,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..820621e 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
@@ -2213,6 +2275,58 @@ man_output_extra_env (struct management *man)
gc_free (&gc);
}
+static bool
+validate_peer_info_line(const char *line)
+{
+ uint8_t c;
+ int state = 0;
+ while ((c=*line++))
+ {
+ switch (state)
+ {
+ case 0:
+ case 1:
+ if (c == '=' && state == 1)
+ state = 2;
+ else if (isalnum(c) || c == '_')
+ state = 1;
+ else
+ return false;
+ case 2:
+ if (isprint(c))
+ ;
+ else
+ return false;
+ }
+ }
+ return (state == 2);
+}
+
+static void
+man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac)
+{
+ char line[256];
+ if (man->persist.callback.get_peer_info)
+ {
+ const char *peer_info = (*man->persist.callback.get_peer_info) (man->persist.callback.arg, mdac->cid);
+ if (peer_info)
+ {
+ struct buffer buf;
+ buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info));
+ while (buf_parse (&buf, '\n', line, sizeof (line)))
+ {
+ chomp (line);
+ if (validate_peer_info_line(line))
+ {
+ msg (M_CLIENT, ">CLIENT:ENV,%s", line);
+ }
+ else
+ msg (D_MANAGEMENT, "validation failed on peer_info line received from client");
+ }
+ }
+ }
+}
+
void
management_notify_client_needing_auth (struct management *management,
const unsigned int mda_key_id,
@@ -2226,6 +2340,7 @@ management_notify_client_needing_auth (struct management *management,
mode = "REAUTH";
msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
man_output_extra_env (management);
+ man_output_peer_info_env(management, mdac);
man_output_env (es, true, management->connection.env_filter_level);
mdac->flags |= DAF_INITIAL_AUTH;
}
@@ -2402,7 +2517,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 +2525,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 +2622,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 +2640,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 +2728,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 +3142,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..6a9ccd8 100644
--- a/manage.h
+++ b/manage.h
@@ -164,12 +164,16 @@ struct management_callback
const char *reason,
const char *client_reason,
struct buffer_list *cc_config); /* ownership transferred */
+ char *(*get_peer_info) (void *arg, const unsigned long cid);
#endif
#ifdef MANAGEMENT_PF
bool (*client_pf) (void *arg,
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 +506,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 123ff48..1f7f616 100644
--- a/misc.c
+++ b/misc.c
@@ -1406,6 +1406,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?
@@ -1414,6 +1417,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 b468285..d5ad774 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/multi.c b/multi.c
index 7a06bd0..dc26a02 100644
--- a/multi.c
+++ b/multi.c
@@ -2605,6 +2605,20 @@ management_client_auth (void *arg,
buffer_list_free (cc_config);
return ret;
}
+
+static char *
+management_get_peer_info (void *arg, const unsigned long cid)
+{
+ struct multi_context *m = (struct multi_context *) arg;
+ struct multi_instance *mi = lookup_by_cid (m, cid);
+ char *ret = NULL;
+
+ if (mi)
+ ret = tls_get_peer_info (mi->context.c2.tls_multi);
+
+ return ret;
+}
+
#endif
#ifdef MANAGEMENT_PF
@@ -2645,6 +2659,7 @@ init_management_callback_multi (struct multi_context *m)
#ifdef MANAGEMENT_DEF_AUTH
cb.kill_by_cid = management_kill_by_cid;
cb.client_auth = management_client_auth;
+ cb.get_peer_info = management_get_peer_info;
#endif
#ifdef MANAGEMENT_PF
cb.client_pf = management_client_pf;
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 3a2a6c5..b78158e 100644
--- a/options.c
+++ b/options.c
@@ -196,6 +196,9 @@ static const char usage_message[] =
" Add 'bypass-dns' flag to similarly bypass tunnel for DNS.\n"
"--redirect-private [flags]: Like --redirect-gateway, but omit actually changing\n"
" the default gateway. Useful when pushing private subnets.\n"
+#ifdef ENABLE_PUSH_PEER_INFO
+ "--push-peer-info : (client only) push client info to server.\n"
+#endif
"--setenv name value : Set a custom environmental variable to pass to script.\n"
"--setenv FORWARD_COMPATIBLE 1 : Relax config file syntax checking to allow\n"
" directives for future OpenVPN versions to be ignored.\n"
@@ -761,7 +764,9 @@ void
uninit_options (struct options *o)
{
if (o->gc_owned)
- gc_free (&o->gc);
+ {
+ gc_free (&o->gc);
+ }
}
#ifdef ENABLE_DEBUG
@@ -1346,6 +1351,9 @@ show_settings (const struct options *o)
SHOW_INT (transition_window);
SHOW_BOOL (single_session);
+#ifdef ENABLE_PUSH_PEER_INFO
+ SHOW_BOOL (push_peer_info);
+#endif
SHOW_BOOL (tls_exit);
SHOW_STR (tls_auth_file);
@@ -1412,6 +1420,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 *
@@ -1924,6 +2063,9 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
MUST_BE_UNDEF (transition_window);
MUST_BE_UNDEF (tls_auth_file);
MUST_BE_UNDEF (single_session);
+#ifdef ENABLE_PUSH_PEER_INFO
+ MUST_BE_UNDEF (push_peer_info);
+#endif
MUST_BE_UNDEF (tls_exit);
MUST_BE_UNDEF (crl_file);
MUST_BE_UNDEF (key_method);
@@ -2095,7 +2237,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 +2254,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 +2268,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 +3782,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;
@@ -5514,6 +5681,13 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
options->single_session = true;
}
+#ifdef ENABLE_PUSH_PEER_INFO
+ else if (streq (p[0], "push-peer-info"))
+ {
+ VERIFY_PERMISSION (OPT_P_GENERAL);
+ options->push_peer_info = true;
+ }
+#endif
else if (streq (p[0], "tls-exit"))
{
VERIFY_PERMISSION (OPT_P_GENERAL);
diff --git a/options.h b/options.h
index ebff532..240f3bb 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;
@@ -495,6 +520,10 @@ struct options
/* Allow only one session */
bool single_session;
+#ifdef ENABLE_PUSH_PEER_INFO
+ bool push_peer_info;
+#endif
+
bool tls_exit;
#endif /* USE_SSL */
@@ -710,4 +739,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/push.c b/push.c
index d05cc1d..9ddc900 100644
--- a/push.c
+++ b/push.c
@@ -102,8 +102,8 @@ send_auth_failed (struct context *c, const char *client_reason)
schedule_exit (c, c->options.scheduled_exit_interval, SIGTERM);
len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
- if (len > TLS_CHANNEL_BUF_SIZE)
- len = TLS_CHANNEL_BUF_SIZE;
+ if (len > PUSH_BUNDLE_SIZE)
+ len = PUSH_BUNDLE_SIZE;
{
struct buffer buf = alloc_buf_gc (len, &gc);
@@ -171,7 +171,7 @@ bool
send_push_reply (struct context *c)
{
struct gc_arena gc = gc_new ();
- struct buffer buf = alloc_buf_gc (TLS_CHANNEL_BUF_SIZE, &gc);
+ struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc);
struct push_entry *e = c->options.push_list.head;
bool multi_push = false;
static char cmd[] = "PUSH_REPLY";
diff --git a/route.c b/route.c
index 7ab82ff..2635afd 100644
--- a/route.c
+++ b/route.c
@@ -2186,7 +2186,7 @@ get_bypass_addresses (struct route_bypass *rb, const unsigned int flags) /* PLA
#endif
-#if AUTO_USERID
+#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
#if defined(TARGET_LINUX)
diff --git a/route.h b/route.h
index 2def1d2..c5cbb7c 100644
--- a/route.h
+++ b/route.h
@@ -174,7 +174,7 @@ bool get_default_gateway (in_addr_t *ip, in_addr_t *netmask);
#define TLA_LOCAL 2
int test_local_addr (const in_addr_t addr);
-#if AUTO_USERID
+#if AUTO_USERID || defined(ENABLE_PUSH_PEER_INFO)
bool get_default_gateway_mac_addr (unsigned char *macaddr);
#endif
diff --git a/ssl.c b/ssl.c
index bf6c1a6..c5f2131 100644
--- a/ssl.c
+++ b/ssl.c
@@ -2580,6 +2580,8 @@ tls_multi_free (struct tls_multi *multi, bool clear)
#ifdef MANAGEMENT_DEF_AUTH
man_def_auth_set_client_reason(multi, NULL);
+
+ free (multi->peer_info);
#endif
if (multi->locked_cn)
@@ -3128,6 +3130,14 @@ write_string (struct buffer *buf, const char *str, const int maxlen)
}
static bool
+write_empty_string (struct buffer *buf)
+{
+ if (!buf_write_u16 (buf, 0))
+ return false;
+ return true;
+}
+
+static bool
read_string (struct buffer *buf, char *str, const unsigned int capacity)
{
const int len = buf_read_u16 (buf);
@@ -3139,6 +3149,33 @@ read_string (struct buffer *buf, char *str, const unsigned int capacity)
return true;
}
+static char *
+read_string_alloc (struct buffer *buf)
+{
+ const int len = buf_read_u16 (buf);
+ char *str;
+
+ if (len < 1)
+ return NULL;
+ str = (char *) malloc(len);
+ check_malloc_return(str);
+ if (!buf_read (buf, str, len))
+ {
+ free (str);
+ return NULL;
+ }
+ str[len-1] = '\0';
+ return str;
+}
+
+void
+read_string_discard (struct buffer *buf)
+{
+ char *data = read_string_alloc(buf);
+ if (data)
+ free (data);
+}
+
/*
* Authenticate a client using username/password.
* Runs on server.
@@ -3355,6 +3392,73 @@ key_method_1_write (struct buffer *buf, struct tls_session *session)
}
static bool
+push_peer_info(struct buffer *buf, struct tls_session *session)
+{
+ struct gc_arena gc = gc_new ();
+ bool ret = false;
+
+#ifdef ENABLE_PUSH_PEER_INFO
+ if (session->opt->push_peer_info) /* write peer info */
+ {
+ struct env_set *es = session->opt->es;
+ struct env_item *e;
+ struct buffer out = alloc_buf_gc (512*3, &gc);
+
+ /* push version */
+ buf_printf (&out, "IV_VER=%s\n", PACKAGE_VERSION);
+
+ /* push platform */
+#if defined(TARGET_LINUX)
+ buf_printf (&out, "IV_PLAT=linux\n");
+#elif defined(TARGET_SOLARIS)
+ buf_printf (&out, "IV_PLAT=solaris\n");
+#elif defined(TARGET_OPENBSD)
+ buf_printf (&out, "IV_PLAT=openbsd\n");
+#elif defined(TARGET_DARWIN)
+ buf_printf (&out, "IV_PLAT=mac\n");
+#elif defined(TARGET_NETBSD)
+ buf_printf (&out, "IV_PLAT=netbsd\n");
+#elif defined(TARGET_FREEBSD)
+ buf_printf (&out, "IV_PLAT=freebsd\n");
+#elif defined(WIN32)
+ buf_printf (&out, "IV_PLAT=win\n");
+#endif
+
+ /* push mac addr */
+ {
+ bool get_default_gateway_mac_addr (unsigned char *macaddr);
+ uint8_t macaddr[6];
+ get_default_gateway_mac_addr (macaddr);
+ buf_printf (&out, "IV_HWADDR=%s\n", format_hex_ex (macaddr, 6, 0, 1, ":", &gc));
+ }
+
+ /* push env vars that begin with UV_ */
+ for (e=es->list; e != NULL; e=e->next)
+ {
+ if (e->string)
+ {
+ if (!strncmp(e->string, "UV_", 3) && buf_safe(&out, strlen(e->string)+1))
+ buf_printf (&out, "%s\n", e->string);
+ }
+ }
+
+ if (!write_string(buf, BSTR(&out), -1))
+ goto error;
+ }
+ else
+#endif
+ {
+ if (!write_empty_string (buf)) /* no peer info */
+ goto error;
+ }
+ ret = true;
+
+ error:
+ gc_free (&gc);
+ return ret;
+}
+
+static bool
key_method_2_write (struct buffer *buf, struct tls_session *session)
{
ASSERT (session->opt->key_method == 2);
@@ -3388,6 +3492,16 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
goto error;
purge_user_pass (&auth_user_pass, false);
}
+ else
+ {
+ if (!write_empty_string (buf)) /* no username */
+ goto error;
+ if (!write_empty_string (buf)) /* no password */
+ goto error;
+ }
+
+ if (!push_peer_info (buf, session))
+ goto error;
/*
* generate tunnel keys if server
@@ -3534,11 +3648,13 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
bool s2 = true;
char *raw_username;
+ bool username_status, password_status;
/* get username/password from plaintext buffer */
ALLOC_OBJ_CLEAR_GC (up, struct user_pass, &gc);
- if (!read_string (buf, up->username, USER_PASS_LEN)
- || !read_string (buf, up->password, USER_PASS_LEN))
+ username_status = read_string (buf, up->username, USER_PASS_LEN);
+ password_status = read_string (buf, up->password, USER_PASS_LEN);
+ if (!username_status || !password_status)
{
CLEAR (*up);
if (!(session->opt->ssl_flags & SSLF_AUTH_USER_PASS_OPTIONAL))
@@ -3559,6 +3675,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
/* call plugin(s) and/or script */
#ifdef MANAGEMENT_DEF_AUTH
+ /* get peer info from control channel */
+ free (multi->peer_info);
+ multi->peer_info = read_string_alloc (buf);
+
if (man_def_auth == KMDA_DEF)
man_def_auth = verify_user_pass_management (session, up, raw_username);
#endif
diff --git a/ssl.h b/ssl.h
index e909c43..5eeca21 100644
--- a/ssl.h
+++ b/ssl.h
@@ -432,6 +432,9 @@ struct tls_options
#ifdef ENABLE_OCC
bool disable_occ;
#endif
+#ifdef ENABLE_PUSH_PEER_INFO
+ bool push_peer_info;
+#endif
int transition_window;
int handshake_window;
interval_t packet_timeout;
@@ -618,6 +621,12 @@ struct tls_multi
*/
char *client_reason;
+ /*
+ * A multi-line string of general-purpose info received from peer
+ * over control channel.
+ */
+ char *peer_info;
+
/* Time of last call to tls_authentication_status */
time_t tas_last;
#endif
@@ -721,6 +730,12 @@ void tls_deauthenticate (struct tls_multi *multi);
#ifdef MANAGEMENT_DEF_AUTH
bool tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth, const char *client_reason);
+
+static inline char *
+tls_get_peer_info(const struct tls_multi *multi)
+{
+ return multi->peer_info;
+}
#endif
/*
diff --git a/syshead.h b/syshead.h
index 049484e..7cc4dba 100644
--- a/syshead.h
+++ b/syshead.h
@@ -629,6 +629,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.
*/
@@ -651,10 +667,8 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
- * Support "connection" directive
+ * Do we support pushing peer info?
*/
-#if ENABLE_INLINE_FILES
-#define ENABLE_CONNECTION 1
-#endif
+#define ENABLE_PUSH_PEER_INFO
#endif
diff --git a/version.m4 b/version.m4
index 5723d3c..8df30d5 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.1i])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])